1768 | | | : ee
| PROGRAMMING YOUR OWN |
DVENTURE GAMES IN
Adventures are played with Caps Lock off
You can break out of programs with Control-C or Control-Shift-@
Notes in red added by Michael Bean, September 2022
A note about disk Drive numbers in file names and commands:
Pascal refers to Drive 1 and 2 as Volumes #4: and #5:. (The number
sign and colons are usually typed as part of the drive name, but
not always.) If no volume number is specified, Pascal defaults to
using volume #4: Pascal can also be directed to use a specific disk,
regardless of which drive it’s in, by typing the name of the disk
instead of the drive volume (such as APPLEO: Again, the colon is
typically typed as part of the disk name.)
As written, the programs and commands in this book do not
specify a drive volume, and Pascal expects any needed files to be
on the disk which is in Drive 1. When entering the programs or
commands, it will often be more convenient to tell Pascal to look
on Drive 2 (Volume #5:) instead.
Examples:
‘#5:a2.db80.x’
(*$i#5:mini1 .text*)
{Su#5:mt1.code}
Edit what file? #5:mtadvent
Alternatively you can specify the name of the disk on which a file is
stored. This way no matter which drive the disk is in, Pascal will find
the files when it needs them, as long as the specified disk is in one
of the drives. However if the disk name is ever changed, or if the
file is copied to a different disk, Pascal won't be able to find the
files.
Examples:
‘diskname:a2.db80.x’
(*Sidiskname:mini1 .text*)
{Sudiskname:mt1.code}
Edit what file? diskname:mtadvent
Pages 25, 81, 203, 207, 209, 224, 225, 233, 234, 261, 262, 263, 273,
274, 282 and 283 utilize the “include” or “uses” instructions.
Specifying a disk name or drive volume here is only important
during the Compiling and Linking process, and does not affect the
final game.
Pages 83 and 232 refer to description database files used by
Adventure 2 and 3. Whatever volume number or disk name is used
here is hard-coded into the game, and Pascal will not look
elsewhere. It might be best to leave these as written, and ensure
that all Adventure files will be in Drive 1 when played.
PROGRAMMING YOUR OWN
ADVENTURE GAMES
IN PASCAL
PROGRAMMING YOUR OWN
ADVENTURE GAMES
IN PASCAL
BY RICHARD C. VILE, JR.
[TaB) TAS BOOKS Inc.
BLUE RIDGE SUMMIT, PA. 17214
FIRST EDITION
FIRST PRINTING
Copyright© 1984 by TAB BOOKS Inc.
Printed in the United States of America
Reproduction or publication of the content in any manner, without express
permission of the publisher, is prohibited. No liability is assumed with respect to
the use of the information herein.
Library of Congress Cataloging in Publication Data
Vile, Richard C., 1943-
Programming your own adventure games in Pascal
Includes index.
1. Computer games. 2. PASCAL (Computer program
language) |. Title.
GV1469.2.V55 1984 794.8'2 84-8577
ISBN 0-8306-0768-4
ISBN 0-8306-1768-X (pbk.)
Cover illustration by Larry Selman
Contents
Introduction
The Elements of Adventure Games
Rooms or Locations—Objects and Treasures—Beings and Monsters—Problems
Notations for Describing Adventures
Rooms or Locations—Travel Indicators— Problems—Objects— Beings and Monsters—Embellishing the
Map—Map Layout and Organization—Examples
Adventures and Problem Solving
Clues to Problem Solutions—Problem Difficulty: From the Obvious to the Obscure—Repeatability—
Logic— Surprise
UCSD Pascal Review
What You Should Know about Pascal—What Is the UCSD System?
Preview of Adventure 1
Preview Chapters—Preview of Adventure 1—Chapter Previews
Pascal Adventure 1
Listing 6-1. Pascal Miniadventure
Representing the Map
Representing the Adventure Map—Enumerated Types in Pascal
viii
12
15
23
54
15
16
17
18
1g
20
21
22
23
Controlling the Play
Case Statements in Pascal—Controlling the Adventure Game
Mazes in the Middle
Local Declarations—Local Procedures and Functions
Other Techniques Used in Adventure 1
Preview of Adventure 2
Maps, Diagrams, and Code Outlines—Chapter Previews
Pascal Adventure 2
Command Processing in Adventure 2
An Overview of the Command Handling Code—Command Processing in Detail
Carry and Drop: Pascal Sets
What Are Sets and How Can They Be Used?
Problems in Adventure 2
Events—Boolean Expressions and Events—Problems in Adventure 2
Other Techniques Used in Adventure 2
Counting Turns—Displaying the Contents of a Set—Simplification of Travel—The Use of File
Variables—Scoring Your Adventures— The Help Command—The Lamp and the Light Command—The
Dig Command—The Eat Command—The Ogre
UCSD Pascal Development Techniques
Managing Your Files Effectively—UCSD System Tricks and Pitfalls
Preview of MAKEDESC and BROWSE
Database Concepts—Previews of Chapters 19 through 24
MAKEDESC and BROWSE
Listing 19-1. Make80 Database Generator—Listing 19-2. Browse80 Database Snooper
Creating a Database of Descriptions
MAKEDESC: The Descriptions Generator—How to Use MAKEDESC Instruction Lines—Ditto Lines—
Continuation File Lines— Running MAKEDESC
Random Access Files in UCSD Pascal
What Are Random and Sequential Files?—The File Creating Program—The File Reading Program—
Random Access—The BROWSE Program—Using Descriptions Databases in Your Adventures
The Structure of Adventure Databases
Index Files and the Descriptions Index—Using the Descriptions Databases in Adventure Games— The
BROWSE Program for Previewing Databases
Programming Techniques Used in MAKEDESC
Symbol Tables—Lookup Techniques: The Linear Search—Hashing: A More Efficient Search
Technique —The Hash Table Lookup Technique
59
63
66
68
78
115
125
130
135
139
145
147
166
173
177
182
24
25
26
27
28
29
30
What Else Can You Put on Disk?
Other Descriptions—Putting the Whole Program on Disk: Adventure Interpreters
Preview of Adventure 3
Outlines, Diagrams, and Maps—Chapter Previews
The Listings of Adventure 3
Listing 26-1. Adventure 3 Main Program—Listing 26-2. Location Procedure Unit—Listing 26-3. Com-
mands 1 Unit—Listing 26-4. Commands 2 Unit—Listing 26-5. Commands 3 Unit—Listing 26-6. Problems
Unit—Listing 26-7. Adventure 3 Data
Larger Programs: Using UCSD Units
Why Units?—The Units in Adventure 3—Units: Syntax and Semantics—Linking Programs that Have
Units—Maintaining a Program Written with Units
Problems in Adventure 3
The Probs Unit—The Problems in Adventure 3—Implementing Adventure 3 Problems
Other Techniques Used in Adventure 3
Putting More into the Database—Coding Techniques to Get Around UCSD Limitations—Trickery in
Showgoodies
Writing Your Own Adventures
Extending Adventure 3—Using a Skeleton Adventure—A Systematic Approach to Writing Adventures—
Farewell
Appendix A Skeleton Adventure Program
Appendix B_- The Adventure 2 Database
Appendix C The Adventure 3 Database
Index
190
192
203
239
245
250
254
261
286
292
304
Introduction
Welcome to the world of adventure games and Pas-
cal. The purpose of this book is to teach you how to
use the Pascal programming language to create
adventure games on your home computer. I shall
cover topics related both to the design and creation
of the games themselves and to the style and use of
the Pascal language. I shall concentrate on the style
of adventure game referred to as a text adventure. I
shall use the UCSD implementation of Pascal,
which is widely available on microcomputers.
ADVENTURE GAMES
Adventure originated in the 1970s. The first
adventure was a game written by Don Woods and
Willie Crowther. The language used was FOR-
TRAN and the computer used was a PDP-10, a
mainframe computer common in universities and
research laboratories. The game was an exercise in
problem solving, artificial intelligence, and simula-
tion. The main goal of the original adventure was to
be a problem-solving exercise.
The original adventure found its way onto
many different computer installations. Since it was
viii
written in FORTRAN, people moved it to other
computers besides the PDP-10. It was rewritten in
other FORTRAN dialects and in other languages as
well. In the early 1980s, it first became available for
microcomputers. Microsoft, Inc. as well as other
software companies adapted the game for use on
personal microcomputers such as the Apple II and
the TRS-80.
Since the advent of adventure on microcom-
puters, an entire subindustry of adventure game
software producers has been created. Many com-
panies created and sold adventures in the spirit of
the original adventure. Most notable of these is
probably the series of games known as Zork. Zork
was also originally written on a large computer.
The authors, several M.I.T. students, created a
company called Infocom and began marketing Zork
and its descendants.
A programmer from Stromberg-Carlson in
Florida, Scott Adams, created a system for writing
adventure games on the TRS-80. He published an
article in Creative Computing magazine describing
the system and went on to create and market sev-
eral of his own adventure games. These games and
Adams’ company, Adventure International have be-
come industry standards.
A company in California, now known as Sierra
On-Line Systems, created several unusual adven-
tures for the Apple II. What made these adventures
unusual was that they used the Apple high-
resolution graphics display to show the player a
picture of each location in the adventure as it was
reached. Thus, textual descriptions and creative
prose was replaced by graphics artistry. Several
companies have now started writing and marketing
adventure games in this style. Such adventures are
referred to as hi-res adventures in honor of the
original implementation.
Several adventure games that mimic the style
and approach of the noncomputerized Dungeons and
Dragons fantasy role-playing games are avail-
able. Dungeons and Dragons was admittedly the
inspiration for the original adventure. However,
the style of original adventure was unlike that of D
and D. In the D and D style games characters are
created or “rolled.” (The terminology is derived
from the fact that several different kinds of dice are
used in the character creation process.) The
characters have attributes such as strength, intelli-
gence, wisdom, and charisma, with numerical val-
ues attached. These attributes play an integral part
in the progress of the game.
In this book I will deal strictly with text-style
adventures in the spirit of original adventure. The
emphasis will be on the creation and solving of
interesting problems. The nature of adventure
game problems is discussed in detail. The methods
of representing and handling problems using Pascal
programming techniques are given extensive
coverage in the text. It is my belief that the scope
for creativity in text adventures is great. There are
as many adventures out there waiting to be in-
vented as there are adventure and fantasy novels
waiting to be written. The range of possibilities is
limited only by your imagination and your pro-
gramming skills.
PASCAL
After BASIC, Pascal is the most popular pro-
gramming language available for personal and mi-
crocomputers. It has been called by some the
“language of the 80s.” Since its creation in the late
1960s, it has spread throughout the world, growing
in popularity and acceptance each year. When a
committee was formed in late 1979 to investigate
the standardization of the language, it attracted the
attention of many of the leading companies in the
computer and electronics fields. The interest in
obtaining a language standard was almost unpre-
cedented in the computer field.
In addition to being popular, Pascal is excel-
lent for programming. Adventure game writing in
Pascal is a satisfying experience as I hope to dem-
onstrate. The features of the language greatly
simplify the process of translating an adventure
game into computer form. The existence of the set
as a primitive data type in the Pascal language
makes implementing the carry and drop commands
almost a snap. That is just one example. Many more
will appear during the course of our investigation.
Even though Pascal has been standardized by
the International Standards Organization through
the efforts of the British Standards Institute, the
Joint ANSI/IEEE Pascal Committee in the United
States, and computer standards committees in
many other countries, there remain a large number
of incompatible implementations of the language. In
the microcomputer world, one implementation in
particular has become immensely popular. That is
the UCSD (University of California at San Diego)
Pascal P-System implementation. It is not just a
translator for the Pascal language, but an entire
software development environment for use on
micro and minicomputers. I shall use the UCSD
implementation for all the examples in this book. If
you use a different version of Pascal, you may have
to make minor changes to implement the programs
found here.
I am going to assume that you are familiar with
Pascal. You should know how to write Pascal pro-
grams and should have written at least one or two of
your own. You should have a textbook or reference
that you can consult regarding matters of language
syntax. I shall not attempt to teach the Pascal
language from the ground up. There is a discussion
of prerequisites in more detail in Chapter 4.
As you read this book, you will learn new
programming techniques in Pascal. You should
study the sample programs carefully—reading
programs is almost as good a way to learn pro-
gramming as writing programs is. Then you should
reread the programs and try modifying them in
simple ways. Finally, you should attempt to write
your own adventure games using the techniques
described.
ORGANIZATION OF THE BOOK
The book itself consists of five sections:
1. Adventure Game Concepts and Design—
Chapters 1-5.
2. Adventure 1: Simple Pascal for Adventure
Games—Chapters 6-11.
3. Adventure 2: A Complete Adventure Game in
Pascal—Chapters 12-18.
4. MAKEDESC and BROWSE: A Simple Da-
tabase for Adventure Games—Chapters 19-25.
5. Adventure 3: Advanced Adventure Game
Techniques in Pascal—Chapters 26-30.
Section 1 deals with general adventure game
ideas. You should read it regardless of your level of
experience in Pascal. Chapter 4 tells you about
Pascal and the UCSD system and should help you
decide how much you need to know before starting
serious study of the book.
If you are a beginner at Pascal, you should read
all the sections carefully. If you are an intermediate
Pascal programmer—you have taken a course in the
language or have programmed in Pascal for at least
a year—you may skim Section 2 and start reading in
detail in Section 3. If you are an experienced Pascal
programmer—two years or more of programming
in the language —you may skim Sections 2 and 3 and
start serious study with Section 4. If you have some
UCSD experience, you may not need to read
Chapter 17, which deals with the effective use of
the UCSD system for developing Pascal programs.
The appendices of the book treat miscellane-
ous topics and include listings of the text of the
descriptions databases used in Adventures 2 and 3.
The Elements of Adventure Games
In this chapter, I will discuss some of the ingre-
dients necessary for creating adventure games. I
will concentrate on games in the style of the original
adventure; however, the techniques I describe
apply to any adventure game.
A typical adventure game contains certain key
stylistic ingredients that stamp it as an adventure.
Among these are the following:
@ Rooms or Locations
Part of the raison d’etre of adventure is to explore
a cave or similar adventure territory. Hence the
rooms of the cave or the locations of the adven-
ture territory and their interconnections form an
important part in the design of the adventure.
@ Objects and Treasures
Most adventure games contain treasures. One of
the objectives of these games is to locate the
treasures and bring them to some “safe” place in
the game map. Creating interesting treasures
and hiding them in unusual ways adds to the
excitement of inventing and playing new adven-
tures.
In addition to treasures, other objects may play a
part in a typical adventure. For example, in the
original adventure there are many objects that
are not treasures but are necessary in order to
make progress in the game. In this game, for
example, both a little bird and a cage must be
dealt with. It is the adventure writer’s obligation
to constantly invent new twists to the nature and
use of objects.
@ Beings and Monsters
Adventure games usually contain other beings
that the player may encounter. Usually these
beings are adversaries, such as the fierce green
snake, the troll, the dragon, and the pirate of the
original adventure. However, there is absolutely
no reason why a being in an adventure game could
not be an ally as well as an adversary.
@ Special conditions and Problems to solve.
Part of the difficulty in winning adventure games
lies in the problems that are incorporated. The
player does not merely locate the treasure, carry
it out, and win! On the contrary, various obsta-
cles must be overcome. Many times ‘dealing with
these obstacles is a prerequisite even to locating
a given treasure. Here is where the full scope of
creativity and originality come into play in mak-
ing a good adventure.
There are various styles of “problems” in ad-
venture games. Some players relish a fight in
which there are odds of losing—for example, the
dwarfs in original adventure, who, if you are
careless, can kill you. On the other hand, some
players prefer a strictly logical challenge —
problems that can be solved totally with the in-
tellect and involve no chance factors at all.
Now that I have enumerated the typical com-
ponents of an adventure game, I will go into more
detail in each category. The discussions below are
intended to start your own creative processes
going. They are not intended as a stock from which
you merely choose a new combination to create
your own adventure. To be truly successful at
writing adventure games, in Pascal or whatever
language, you must exercise your own individual
creative powers. A definition of creativity that I
especially like is the following:
To be creative—look around you at what
everybody else is doing. Then don’t do that!
ROOMS OR LOCATIONS
The classical environment for an adventure
game is an underground warren of caves, tunnels,
or mazes. There is something romantic about
exploring an underground empire, looking for trea-
sure. Many variations on the cave theme are possi-
ble. It is still possible to be creative by inventing
new and unusual rooms. To take an analogy from
music, Bach, Mozart, and Beethoven all used the
same musical scales and keys to create their musi-
cal works. However, each composer produced art
that was radically different from the other. There
are many underground worlds still left to be in-
vented that are as different from original adventure
as Beethoven was from Bach!
What then are some general guidelines for
creating the rooms of a new adventure? Let us
consider a few:
Make Sure There is Variety. There should
be a difference in character from one location to the
next in a good adventure. There should be large
rooms and small rooms. There should be rooms
with lots of interest and others that are merely
stops along the way. There should be objects
spread around in a variety of rooms. It is probably a
bad idea to put all the objects in one or two locations
that are off in some distant and obscure corner of the
map. The player of an adventure game likes to see
progress being made.
By the same token, not all locations should be
easy to find. Not all objects and treasures should be
located right out in the open. Thus, even when the
player reaches a given room, there may still be
aspects of that room that are only revealed after the
player solves a problem or two.
A good adventure map will have large ter-
ritories that are easily accessible. It will also have
one or two “chunks” of locations that are separated
from the rest of the map by a narrow access path.
There are various ways to achieve this goal:
@ Make the only entrance to the “chunk” via a room
that has a large number of exits. This means that
the player has to try a large number of exits
before finding the right one.
@ Guard the only entrance to the “chunk”. That is,
require that the player solve some sort of prob-
lem in order to gain access to the entrance. An
example of this sort of approach is the use of the
fierce green snake in original adventure. It
guards the only passageway to a large part of the
cave. The only way to get through is to dispose
of the snake.
@ Make the first part of the “chunk” dull and boring
so that the player is less likely to explore further
in that direction.
Require Exploration. Don’t create rooms in
which everything is obvious from the very first
description. There should always be rooms in
which it takes some work to find out everything
there is to know about them. There are many ways
to accomplish this end. Some of them will be de-
scribed later in this chapter. Here are just a few of
them.
@ Require that a special command be given in order
to obtain the full description of the room. There
might be hints that this is necessary, either in the
ordinary description of the room or in some other
aspect of the game.
@ Require that certain conditions be met before
giving the full description of the room or its
contents. For example, the adventurer might be
forced to possess a certain object or have ac-
complished a certain goal before being able to
fully discern the nature of a given room.
@ Require the discovery of information about a
room from someplace else in the adventure. For
example, a se ‘et map giving necessary infor-
mation might be found somewhere totally re-
mote from the location in question.
OBJECTS AND TREASURES
A typical adventure game has a myriad of trea-
sures that must be located. It also has a variety of
objects, some useful and some merely window
dressing. To be different, you might try an adven-
ture that has just a single treasure. But, usually it
will be wise to stick to tradition. In order to invent
interesting treasures to find, you must simply be
creative. As mentioned above, this involves in-
venting treasures that no one else has used before.
Likewise, there are millions of objects that have
never played a role in an adventure game. So being
creative should be easy!
It should go without saying, but let’s say it
anyway:
Treasures should have some value.
This need not be an intrinsic value, but might be
value derived in some way from the circumstances
of the game. For example, the game might be set in
some imaginary kingdom in which dandelions were
of inestimable value. In sucha situation, dandelions
would be a legitimate treasure. Of course, it would
be up to you, the adventure writer, to establish the
value of dandelions. By inventing imaginary
worlds, anything could potentially become a trea-
sure. The ring of truth comes from the manner in
which you set about convincing the adventurer that
something ordinary could be of value in an imagi-
nary setting. There is considerable scope here for
inventiveness. As a challenge, try imagining
something that you would ordinarily consider to-
tally outrageous in the role of a treasure. Then try
to invent an adventure setting and a description that
makes that something utterly invaluable.
Objects are even easier to deal with than trea-
sures. There are no requirements at all concerning
objects. An object may be present simply because
you will it to be so. No other explanation is neces-
sary. In fact, it is your obligation to have at least
some objects that have no use in the game what-
soever. Unless, of course, you are a classical purist
who requires that every element of a game have a
purpose no matter how small. Most of us are not
purists and are quite willing to populate our adven-
tures with stray incidental artifacts. It makes the
game more challenging: the adventurer must dis-
cover, by reason or chance, which objects are useful
and which are not.
Hints for Inventing Objects and Treasures
@ Browse through the dictionary and the ency-
clopedia. You will come across an amazing
amount of material in these works.
@ Play other adventure games. You may get ideas
by enlarging on what has been done before. Try a
new variation on old themes.
@ Read fantasy and science fiction novels for ideas.
Don’t plagiarize, but let your mind roam and wan-
der. Start with what you read and extend by
making new hypotheses and asking “What if?”
BEINGS AND MONSTERS
Good fiction contains good characters. Good
adventure games will have interesting creatures
inhabiting them. Here again, you may allow your
imagination to run wild as you dream up monsters
and other creatures to put into your games.
There are some practical considerations here,
however.
@ Beings, in general, may move around from loca-
tion to location. The creation of moving beings is
generally more difficult than the creation of
creatures that stay in one location throughout
the game.
@ Animate beings exhibit behavior. Places or loca-
tions do not. It is a great challenge to incorporate
behavior and reactions of creatures in your game.
Most adventure games are limited in this regard.
In this treatment, I will have creatures whose
behavior patterns are somewhat limited. Doing a
really serious job of simulating behavior would re-
quire that I delve into advanced topics such as
artificial intelligence, which I do not have the space
to do.
PROBLEMS
To many players, it is the problems in a good
adventure game that provide the true pleasure of
playing them. I shall devote an entire chapter to this
topic as I proceed. One aim will be to discuss not
only the ingredients that go into creating good
problems, but also the programming techniques
needed to bring problem solving to life in Pascal
adventures.
Notations for Describing Adventures
When you are creating an adventure game, it helps
to draw maps. The adventure map summarizes the
game in a concise notation that helps keep you
organized. In this chapter, I describe my own nota-
tion—the one that I use in the maps reproduced
herein. You may end up adopting a system totally
different than mine. That is perfectly acceptable.
The idea is to have some way of describing your
own adventure games.
ROOMS OR LOCATIONS
Most of any adventure map consists of the
rooms or locations in the adventure. They should be
laid out on paper in a rough representation of their
“actual” geographic relationships. You obviously
have to use some conventions here, especially for
dealing with up and down.
Pick a standard symbol for representing a lo-
cation and always write the name of the location
inside the symbol. I like the hexagon or elongated
hexagon shape for most rooms, with circles or ovals
for maze rooms or other crowded locations. You
may also choose to use different symbols depending
on the nature of the location. For example, a rec-
tangle, as opposed to ahexagon, might represent a
room containing a treasure.
TRAVEL INDICATORS
Adventure locations would be quite unin-
teresting if it were not possible to travel between
them. Adventure maps show ways to travel be-
tween locations. Each possible path of travel may
be indicated by a bold line joining the two locations.
Arrowheads at the ends of the lines may be used to
indicate whether or not the direction of travel is
reversible. The absence of an arrowhead means
that it is not possible or permissible to travel along
this route in the direction indicated by the missing
arrowhead.
Directions are part of the descriptions used in
the play of the game itself. Each line of travel should
be marked with a direction indicator. The usual
directions are limited to n, s, e, w, u, and d. Occa-
sionally, a minor compass point, such as NE or SW
may be used.
Some travel paths may only be taken when
5
certain conditions are fulfilled. Some examples of
this are
@ The player is carrying a certain object.
@ The player has solved some problems.
@ The player has removed an obstacle to travel.
These conditions may be indicated on the map
by a brief description of the condition in words. This
description can be written near the affected travel
line or can be placed in a footnote.
PROBLEMS
Much of the fun of adventure games lies in the
problems they pose for the player to solve. I elabo-
rate on this theme in a future chapter. For now, I
only want to show you how to indicate that prob-
lems exist in various places on the adventure map.
Problems may be indicated by a special sym-
bol. I use a balloon letter style question mark,
enclosed in a circle as shown in Fig. 2-1.
?
Fig. 2-1. A problem indicator for adventure game maps.
This notation needs supplementation in order to
distinguish one problem from another. One way is
to assign a number to each of the problems in the
adventure. Write the number of each problem near
the appropriate question mark symbol. In a sepa-
rate place, describe each problem in detail in
words. Another possibility is to give each problema
name analogous to the placenames given to loca-
tions. The name of the problem could then be writ-
ten near the ? symbol instead of a number. I use the
numbering approach and write the number of the
associated problem inside another full circle placed
to the right of the circle containing the ? symbol.
OBJECTS
Objects usually start out at a fixed location at
the beginning of the adventure. All such objects
may be indicated on the map by writing the name of
the object inside the box designating that location.
Of course, this notation describes the adventure at
the start of play. The player is free to pick up
objects and move them around during play.
To indicate that there is a problem associated
with an object, you can place a dash after the name
of the object. Then a small ?, perhaps followed by
the number of the problem, can be written after the
dash. Occasionally, an object will have properties
that are not part of the description of any problem.
This situation can be handled by the use of footnotes
that associate the textual description of the proper-
ties with the name of the object itself.
You may like drawing a cartoon style cloud
around the name of each object. This serves to
make a graphic distinction between the placename
and the object name. While this is not strictly
necessary, some may find it esthetically or psy-
chologically pleasing.
BEINGS AND MONSTERS
These may be considered to be objects. They
are animate objects, true. However, when you are
drawing the adventure map, you can treat them just
as you treat the inanimate objects.
EMBELLISHING THE MAP
The artistically minded may embellish their
adventure maps much further than these simple
instructions indicate. Pictures of locations, mon-
sters, and objects all make interesting viewing.
Perspective drawings of the locations and their re-
lationships is also fun. All of this has nothing per se
to do with programming adventure games, but their
creation may well stimulate the imagination.
MAP LAYOUT AND ORGANIZATION
For complex and detailed adventure games,
you will find that you have to draw a large map. You
won't be able to fit this map on a single sheet of
standard sized paper. If you don’t want to go to the
trouble of locating and purchasing oversized draw-
ing paper, you will need to split your maps into
multiple sheets. When you do this, try to group
your locations into geographically and/or logically
related “clumps.” Then put one clump per page on
your maps. Try to choose the clumps so that there
is a minimum of possible travel paths between the
different clumps. Then the connections between
pages may be indicated with a symbol often used in
drawing complex computer program flowcharts.
The symbol in question is appropriately named the
off-page connector. It looks like a pentagon with a
number in it. The numbers in the pentagons are
used to associate two connectors on different
pages. They do not refer to any numbering of the
map pages themselves. If a straight line leads into
an off-page connector, look for a similar connector
on another page. The matching connector should
have a straight line leading out of it as shown in Fig.
2-2
——
[3
Fig. 2-2. An off-page connector for adventure game maps.
EXAMPLES
Each adventure game included in this book is
mapped in detail using the techniques of this chap-
ter. The resulting maps are included in the preview
chapters that precede the complete listing of the
Pascal source code for the game itself.
Adventures and Problem Solving
The essence of a good adventure lies in the prob-
lems it poses for the user to solve. They must be
varied, original, interesting, challenging without
being impossible, intricate without being arbitrary,
and so on. This is truly an area where your creative
impulses may be vented to their fullest. There is no
cookbook approach to creating good problems.
There are a number of guidelines, however. Let’s
do some exploring and see what we can discover.
CLUES TO PROBLEM SOLUTIONS
For each problem you contrive in an adven-
ture, there should be at least one associated clue or
hint. Such clues may take many forms:
@ They may be hidden in the description of loca-
tions. Such clues should be subtle. In most cases
the hint should be indirect. The player should be
required to make some deduction based on the
clue and other information. Only in .are cases and
for extremely trivial and unimportant problems
should you give away the answer directly.
The player should have some way of telling that
part of a description is in fact a hint. This can be
done by making the hint somehow different in
tone or style. Make it just slightly “jar” the
player into recognizing it.
@ Clues may be written on objects contained in the
adventure. A clue may be written on a wall ona
piece of parchment hidden in a closet or an old
chest, on the bottom of a bottle, or just about
anywhere. It may take an explicit command on
the part of the adventurer to get at such clues.
This gives you a chance to give hints about hints!
You can take this more than one level deep in
some cases. A few problems may even require an
entire chain of clues in order to reveal their solu-
tion.
To get at a message hidden inside a container,
the player should have to open the container,
take out the object on which the message is
written, and explicitly ask to read the message.
How all this is represented in a computer pro-
gram will be discussed later. Keep in mind that
you will have to program your clues—so don’t
get too carried away.
@ Clues may be implicit in the behavior of beings or
entities within the adventure. The way a monster
reacts to certain commands or situations may
reveal weaknesses that the adventurer can
exploit.
When you think of a good problem to include in
your adventure, don’t stop there. Work on ways of
making the problem solution entertaining for the
player. Really good clues will enhance this. Don’t
expect all this to magically come to you the instant
you think of a problem. You should let the problem
roll around in your subconscious for awhile. Write
each problem ona separate sheet of paper and make
notes as ideas come to you. Gradually crystallize
these ideas into hints and clues. As you develop
your adventure story line and structure, come back
to these sheets again and again. Ask yourself if you
can think of more or better clues. The effort you put
into this will pay off in compliments from those who
play your games.
PROBLEM DIFFICULTY: FROM
THE OBVIOUS TO THE OBSCURE
Not all problems in an adventure should be
equally difficult or easy. There should be a variety
of levels of difficulty. There should be enough rela-
tively easy problems in all parts of the game to keep
the player coming back to the harder problems. The
surest way to kill off interest in all but the most
stubborn adventurers is to make all your problems
next to impossible to solve.
How can the difficulty of a problem be esti-
mated? One way is by the number of clues needed in
order to solve the problem. The more information
that must be gathered in order to figure out the
solution, the harder the problem is likely to be to
solve. Another factor is the individual clues them-
selves. A problem that has one very obscure clue is
probably harder to solve than one that has several
easy clues.
When you start writing real adventures, get
others to play them. Then get feedback on the
difficulty levels that you have put into them. If you
have friends that are also writing adventures, so
much the better. Trade adventures back and forth
and then trade ideas about making problems both
interesting and realistic in terms of their level of
difficulty.
REPEATABILITY
There is a choice to be made in creating ad-
venture problems. Do you want the solution to a
problem to have an element of chance or not? For
example, if you have to fight other beings, should
there be an absolutely guaranteed method of win-
ning or should there be some probability, however
small, of failure even in the presence of perfect
logic? This is a choice that you, the adventure
writer, will have to make. It all depends on who will
play your games and what their preferences are.
Absolutely repeatable problems will involve
only logic in their solution. Problems that involve
chance may still involve logic as well. The differ-
ence is that the player is not in complete control of
the outcome. There is a middle ground here. You
may create a problem in which the player may take
certain actions in order to guarantee that the chance
element is ruled out. For example, if the player
attacks acertain monster first, that might guarantee
that there is a completely logical way to avoid being
a victim of the monster. Again, it may require other
kinds of actions on the part of the adventurer to
secure such results. The player may need to get
killed off a few times in order to notice what these
conditions are. Here you have another way of
creating wheels within wheels effects. The player
may through experience learn more and more about
how to solve a problem. There may be progress
through several levels where chance still plays a
part, before the player reaches the absolute logical
pinnacle consisting of the problem solution.
LOGIC
Good problems should allow a player’s true
problem-solving ability to be exercised. The solu-
tion to a problem should not be completely arbi-
trary, requiring the player to make wild or random
9
guesses in order to arrive at a solution. In short,
problems should be logical.
What is logic? For our purposes, it is a system
of reasoning. The player may draw conclusions
about the play of the game. Such conclusions may be
based on information contained directly in the
game. This may take the form of hints as discussed
above. It may be hidden in descriptions of locations
or objects. On the other hand, it may be necessary
to draw conclusions based on common sense; that
is, based on the normal properties of objects or
behavior of objects in a natural setting. For exam-
ple, water makes plants grow; dry wood may be set
on fire with matches; doors must be opened in order
to see what is behind them; and you may have to dig
in order to uncover buried treasure.
In deducing information, it may be necessary
for a player to make guesses. These guesses should
always be reasonable, not arbitrary. And good
guesses might be rewarded with the offer of further
information.
A good way to require the use of logic is by
including more than one hint or clue regarding a
problem’s solution. The player must discover two
or more clues and connect them logically in order to
solve the problem.
SURPRISE
I have just finished emphasizing that problems
should have logical solutions. Having said that, I
can now relax my position a bit and allow for the
element of surprise.
There may be an inherent conflict between
logic and surprise. What is totally logical is not
surprising. What is surprising cannot be totally
logical. What you wish to avoid is not illogical solu-
tions, but arbitrary ones.
There is a fine line between a surprising solu-
tion to a problem and an arbitrary one. To illustrate
this point, consider the original adventure game. In
that game there are two problems that serve as
perfect examples. First consider the one with the
surprising solution.
There is a fierce green snake that bars you
from entering a certain passageway. This tunnel
turns out to be the only entrance to the rest of the
10
cave. Therefore you must find a way to get rid of the
snake. If you have played the game “correctly” up to
that point, you reach the location of the snake car-
rying a cage containing a timid little bird. You try
everything you can think of to get by the snake, but
all efforts fail. What to do next? Now, using logic,
you might engage in the following sort of dialogue:
Q. What means are at my disposal?
A. None.
Q. Really?
A. Well, I do have this bird in the cage.
Q. What might you do with it?
A. I could get it to sing—maybe that would
frighten the snake away.
Q. Oh well, I suppose. Go ahead and try it.
A. OK.
Q. What happened?
A. Absolutely nothing.
Q. Well, what else can be tried?
A. I guess I could let the bird go. Maybe I could
catch the snake in the cage.
Q. Good idea. Go ahead and do it.
A. OK.
Q. What happened?
A. Amazingly enough, the bird drove off the
snake!
Surprise, surprise, surprise! You never thought the
bird could have any positive effect on the snake.
Nonetheless, you did discover the solution by a
process of reasoning and common sense. (You
thought the cage might be useful.)
Now for the problem with the arbitrary solu-
tion. When you get extremely good at the original
adventure, you find that you have accumulated 349
points out of a possible 350. Unless you stumbled
on the way to get the 350th point, you are now faced
with the nasty problem: “How do I get the last
point?” This problem is particularly nasty since its
solution could lie almost anywhere. Some people
spend hours and hours, for example, looking for a
location that they have not visited that might net
them the extra point.
There are some magazines that exist in the
game. They seem to play no role. However, if you
pick them up from their original location and carry
them to Witt’s End, you get the extra point! How
utterly disappointing this turns out to be when you
discover it—truly arbitrary, truly deflating, truly
anticlimactic.
The moral of the story is—try to avoid totally
arbitrary solutions. Don’t make players of your
game guess that your Uncle Harry’s hair is brown in
order to get a point! Don’t require them to perform
random acts in random locations in order to secure
the last point!
11
UCSD Pascal Review
All of the programs presented in this book were
written using the UCSD Pascal system. I used the
version of this system as adapted by Apple Com-
puter Co. for use on the Apple II and Apple J//e.
These programs should work with little if any mod-
ification on any computer using the UCSD P-
System.
In this chapter I intend to accomplish the fol-
lowing:
@ Tell you what you should know about Pascal
before digging in further.
@ Explain to those that are unfamiliar with it what
the UCSD System is.
@ Highlight some of the features of the UCSD Sys-
tem and some of the tricks of using it effectively.
@ Give you some general suggestions that will be
helpful when you start writing your own adven-
tures.
WHAT YOU SHOULD KNOW ABOUT PASCAL
This is not an introduction to Pascal program-
ming. I am going to assume that you know the
12
basics. That means that you have written at least a
few Pascal programs. It means that you are familiar
with the syntax of Pascal and the mechanics of
writing programs. You should at least have an idea
of the meanings of the following terms:
@ Procedures and functions.
@ Block structure.
mw Parameters: actual and formal.
@ Types (user-defined, predefined), type checking.
@ Structured statements, control structures.
@ Declarations.
wm Value parameters, var parameters.
m Files (sequential), get, put, readln, writeln
@ Arrays.
@ Records.
I will be using most of these concepts as well
as some others in the programs. In many cases, I
will give detailed explanations of how a particular
usage works, and why it makes sense in Pascal.
Thus, if you are not an expert, you should be able to
learn more about how to use many Pascal features.
see also page 143, “The #*%%!!? ESCAPE Key”
Just study the example programs diligently and try
to follow the explanations.
I won't start out with the most basic topics,
however. That is why I hope you are already con-
versant with these concepts.
If the preceding list makes you feel tired or
intimidated, you should probably obtain a good be-
ginning textbook on Pascal. Read it in parallel or
slightly ahead of your study of this book. There are
many books already out on Pascal and many more
appearing all the time. The best places to seek them
out are your local computer store or bookstore.
When you buy a book make sure that it is not limited
to a subset of Pascal or to an implementation that is
peculiar to one or two computers.
WHAT IS THE UCSD SYSTEM?
The UCSD Pascal system was originally de-
veloped at the University of California at San
Diego. It was intended to be used in the teaching of
Pascal, and the goal was to implement the system
on a variety of small computers, including mi-
crocomputers. The project was conceived and di-
rected by Dr. Kenneth Bowles, a professor of com-
puter science at UCSD.
The system is just that. It is a complete
software development environment for Pascal, in-
cluding not only a Pascal compiler but also a com-
plete operating system and many utility programs.
When you use the UCSD system, it is in complete
control of your computer. Thus, if you have a sys-
tem with CP/M, you must reboot the system in
order to use UCSD Pascal, and you no longer have
access to CP/M. The same is true for most micros
that support a manufacturer’s proprietary operating
system. For example, when you run the UCSD
system on the Apple II, you must forego access to
the Apple DOS.
The UCSD system has been made into a com-
mercial product by the SofTech Microsystems
company in San Diego. It is now referred to as the
P-System, and it runs on virtually every mi-
crocomputer currently manufactured. The system
is based on an interpreter that executes a pseu-
code known as P-Code (whence the name P-
System). The interpreter communicates with the
hardware and the peripherals of the computer via a
small set of programs known as the BIOS (Basic
Input Output System).
The BIOS includes the machine code neces-
sary for booting the system from a floppy disk. The
booting process involves the reading in of the BIOS
and the P-Code interpreter from the floppy disk.
Then the operating system portion of the UCSD
system is loaded. It is written in P-Code and is
interpreted by the P-Code interpreter. Once it has
been loaded, control is passed to the interpreter,
and off you go.
The UCSD system is one of the early menu-
driven systems. A menu isa list of choices coded by
numbers or letters from which the user chooses.
The UCSD system has a command line that lists the
commands available ina given context and indicates
their abbreviations. You have to know what each
command will accomplish by reading the users’
manuals, because there is no online help facility
available.
In addition to the BIOS, the P-Code interpre-
ter, and the operating system, the UCSD system
provides
@ A full-screen text editor.
@ A filer for manipulating files on the floppy disk.
g A Pascal compiler for compiling Pascal programs
into P-Code
@ A linker for combining certain P-Code files into
programs that are executable by the P-Code in-
terpreter.
@ A librarian program that enables you to create
libraries of reusable pieces of P-Code. These
pieces may then be incorporated into many dif-
ferent programs.
There are other features of the UCSD system,
but these are the ones that I will be using the most.
In order to learn about the UCSD P-System,
you will need the users’ manuals for your particular
implementation. I use the manuals for Apple Pascal
as provided by Apple Computer. There are also
textbooks available that discuss the use of the sys-
tem, although they tend to be paraphrases of the
UCSD documentation, organized differently and
with different diagrams. Still they may in many
13
cases be useful—you will have to decide for your-
self.
This book is not intended to be a tutorial on the
UCSD Pascal system. I assume that you know
enough of the basics to use it. I will, however, give
instructions on some techniques that you may not
14
have picked up in your beginning use of the system.
This will take the form of tips about general use of
the system and some detailed instructions on pre-
paring the programs in this book for actual use on
your computer.
Preview of Adventure 1
In this book, I present substantial Pascal programs.
Adventure games in Pascal tend to be large pro-
grams; there is no getting around that. My approach
will be to introduce you to the goals and content of
each program with a preview chapter. Following
the preview you will find the complete listing of the
Pascal program itself. After that will be several text
chapters explaining the design of the program, the
Pascal features used, and the way the Pascal pro-
gramming techniques relate to the adventure game.
In order to get the most out of each program,
here is how I suggest you read this book:
w Read the outline chapter, noting the mention of
any Pascal features with which you are not al-
ready familiar. Pay particular attention to the
outlines and diagrams. Try to grasp the overall
design of the program.
gw Skim through the program listing in back to front
fashion—see the explanation of how to do this in
the preview of Adventure 1 later in this chapter.
Write down questions you may have about how
the program works. Refer to these questions as
you study the program in detail later on.
Read and study the chapters devoted to explain-
ing the program. As you read, refer frequently to
the actual program listing. If possible enter the
code for the program in your own computer as
you proceed.
@ After reading all the chapters pertaining to the
specific program you are studying, go back and
read the listing of the program once again. This
time, read the program for details. Make notes of
the particular coding techniques that you plan to
use in your own adventures.
@ Finally, design your own adventure game, using
the techniques illustrated by the program you
have finished studying. Implement it using your
own computer and UCSD Pascal system. Com-
pare your finished game to the illustrative pro-
gram in the book. Review your lists of questions,
and see how many you now can answer yourself.
PREVIEW CHAPTERS
Long programs can be intimidating, confusing,
15
and difficult to understand. So I would like to give
you a perspective of a program before plunging into
its details. By starting out with an idea of the overall
structure of a program, you will be able to under-
stand it more quickly. You will be able to fit details
into the overall picture. Concentrating on program
structure from the start will also encourage you to
pay attention to the structure of the Pascal pro-
grams that you write yourself. This is an important
point often overlooked by beginning or relatively
inexperienced programmers.
I will include diagrams that show the static
structure of the program. These diagrams will list
the program elements (declarations, functions and
procedures by name, main program, and so on) in
the order in which they appear in the program list-
ing. In addition to the static structure diagrams, I
will include diagrams for each program revealing its
dynamic structure. These diagrams will be mod-
eled after the style of diagram used in the structured
design technique of programming. Such di-
agrams reveal the relationships between the vari-
ous procedures and functions in the program. They
may also show how various pieces of data used by
the program are passed around and modified by dif-
ferent parts of the program.
(BD
Fig. 5-1. A diagrammatic representation of a procedure call in
structure diagrams.
16
CAD
;
Fig. 5-2. Data connection in a structure diagram: A passes C
to B.
Each procedure or function (as well as the main
program itself) is represented in a structure dia-
gram by an oval. The name of the procedure or
function is written inside the oval. Lines with ar-
rowheads connect various ovals in the diagram. A
line that points from an oval named A to an oval
named B, as shown in Fig. 5-1, indicates that pro-
cedure or function A calls procedure or function B.
That is, the procedure or function A has in its
program text (or code, if you prefer) an invocation
of procedure or function B. Ifthe tail of the line (the
end without the arrowhead) has a diamond shape,
@, it means that the call from A to B is conditional.
The code in A will make some test. Depending on
the outcome of the test, A may or may not call B.
This information can be valuable in debugging a
program; if there is a conditional call to B, but B is
not called when it should be, there is no doubt a
problem in the logic of A’s code.
Some lines that connect ovals will be labeled
with variable names. This means that the data rep-
resented by that variable passes between A and B.
If A provides B with a variable named C, the dia-
gram might look something like the one shown in
Fig. 5-2.
Notice the smaller arrow beside C. The circle
PROGRAM miniadventure;
CONST, TYPE, and VAR declarations:
This section of the program contains the declarations
of the constants, types, and variables used by the
rest of the program.
See Figure 5-4
procedures and functions:
This section consists of all the headers and code for all
the procedures and functions accessible to the
main program block and the outermost level
of the program.
See Figures 5-5 and 5-6
This section of code is the first to be executed.
It is referred to as the main program block. When reading
a Pascal program, you should always start here.
See Figure 5-7.
Fig. 5-3. The code outline for Adventure 1: the program outline.
on the tail of this arrow points toward the provider
of C, in this case A. This information is also of
potential importance. Many bugs in programs are
due to variables somehow obtaining incorrect val-
ues. A structure diagram can help reveal where a
variable could be “going sour.” Careful study of the
diagram with your code at hand often reveals prob-
lems.
Structure diagrams give another viewpoint of a
program, one that is more important in under-
standing how the code works. When you study
structure diagrams, notice how groups of functions
and procedures form logical chunks of a program.
Notice also how each procedure and function ac-
complishes one logical task within a program. As
you program in Pascal, this use of logical structure
should be a conscious goal. It makes debugging and
program modification considerably easier.
In addition to all the diagrams that attempt to
reveal the structure of the program, I shall include a
survey of the chapters that follow the program list-
ing. Each chapter will be described briefly. You will
be given an idea of what to expect from each chap-
ter.
PREVIEW OF ADVENTURE 1
The full listing of Adventure 1 is presented in
Chapter 6. After you finish reading this chapter, you
should read the listing of Adventure 1 in back-to-
front style. What does that mean? In standard Pas-
cal programs, the so-called main program block,
that is, the code that is executed first, is located at
the end of the program text. So when you read a
Pascal program, you should really start at the back.
17
“Seunpeooid puke SUO!OUN} ay} : |, BINJUBAPY JO} aUII]NO apPOd aU! “S
‘puapeap © ¢ ‘yd ‘zmoueu
SUOIJEDO} AU} (WO |AACI}
‘puapeapd 3Ynaa90ud
Huipnjoul) ye uooe 9y}
ajpuey yey} Sainped0lq
dd 3una390ud
‘~moueud 3YNG3I0Ud
*9-G aunBi4 aag ‘saunpadoid puke suol}oUNn}
W901 aiow Jo auo sey yoey ‘azew ay} pue
uap S,a160 ay} ye UOH}Oe au} ajpueY yey} SaINp|ad01q
‘azewd 34Nd390Ud
‘woosas8od 34Nd3I0Ud
‘woo1aaid 34NG390Ud
“WOOJad! “°° ‘AINQNSEA ‘pes
SUO!}E90] BY} (WO }@ACI}
Buipnjoul) ye uoNoe au}
ajpuey yeu} SAINpsd01q
‘ainqnsaad 3uNda90Ud
‘weysd 3UNdaI0Nd
‘uap s,ai60 ay} ye UOH|Oe ay} Buljpuey ul s}sissy
‘uonoeai80 3YNdIIOUd
jsiayja] Sey puewWOd ains Sayew }|
“@SOdNd Siy} JO} PUBWIWOD JO 19}}9] }Suly BY; Je
SOO} }| “A¥2} 0} SAYSIM JaAe|d By} UOHDAJIP 4} SEUIWW9}9q
‘suonoelp :AeMYoIYM NOILONN
‘aweb au} Jo pua au} ye as00s SJjahejd ay} Sayejnojeo
‘UFDILNI *2J09S NOILONN
‘Ke\d jo
Buiuuibaq ay} ye aweb ayy jo aye}s au} jOoyas SAN|eA
jenul ayy “sajqeuen wesBoud ye 0) sanjea subissy
‘azZiJeNu! JYNGIIONd
‘apin6 ay} pue aweb au} 0} 1aAe|d ay} Seonposju|
UONINPOAU! JYNDIIONd
-¢ “Biy
“suolyesejoap eyep ay} :Z aINJUSAPY 40} aUIIJNO apod aU, “p-S ‘BI4
‘aweb ay} Bulunp paind90 OU eAey 410 sAeY
UdIUM S]UBAP JO 494} daay 0} SajqeUeA
payood ‘paddes ‘paddoip
‘SuiAueo ‘Bswpeas ‘ayeme ‘uajea ‘jinb ‘auop
sahejd
ay} Aq uaye} SUN} Jo JaquUNU ay} S}JUNOD
sun}
0} pa}aaes} sey
jakejd ayy yey} aweb ay} ul seoejd ay)
soe} YoIUM ‘swoos Aq paexepul ‘Aeue ue
Paysia
payeoo| si saAejd 9u}
a19UM SayedIpUl SOO! adAj jo ‘anjeA Ss}
uolje 90}
JoXejd ayy Aq pad}
(@y42} 0} UO!}OaJIP) PUBWLWOD SpjOYy
puewwoo
wesBoid ay} Aq pasn sajqeuen
JAAR} JO SUOIDAIIP ajqisSod au} S}juesesdas
SuoljoauIp
| unquaApy ul SUOI}e90| 34} s}Juaseideal
swool
sadA} pauyap-iasn
s]ue}SuOd pauyap-sasn
18
PROCEDURE pogreroom;
PROCEDURE general description;
Local (or nested) procedure
that is used to print
the general description
of the ogre’s demesne.
PROCEDURE pmaze;
Procedure that implements the maze. It is like a small
adventure inside the larger adventure. It uses
numerous local procedures and functions.
FUNCTION bittest: BOOLEAN;
PROCEDURE describe;
PROCEDURE sameplace;
PROCEDURE treasure;
These procedures provide general
support for the action in the maze.
PROCEDURE pm1;
PROCEDURE pm2;
PROCEDURE pm3;
PROCEDURE pm 18;
PROCEDURE pm19;
Procedures pm1, pm2, . . ., pm19 are the location
procedures for the nineteen maze rooms. They are
analogous to the location procedures pstart, pvestibule,
and so on. They handle travel and action inside
the maze itself.
The last part of the listing will contain the main
program block. The code there will invoke other
parts of the program, in particular the procedures
and functions whose text appears earlier in the
listing. This means that as you see references to
procedures and functions in the main program
block, you will have to look for the listings of these
procedures and functions closer to the front of the
program. In turn, these procedures and functions
may refer to other procedures and functions. Un-
less the author of the program has used FORWARD
declarations (which I in general attempt to avoid in
my adventure games), the procedures referred to
will be listed even closer to the start of the listing.
Fig. 5-6. The code outline for Adventure 1:
the procedures with local procedures.
Now you can see what I mean by reading the pro-
gram back-to-front.
Figures 5-3 through 5-7 present the code out-
line of Adventure 1. Figure 5-3 shows the entire
program structure in brief, explaining the major
parts of every Pascal program. It refers to the other
figures that show various parts of the program in
more detail. Figure 5-4 outlines the const, type,
and var declarations of the entire program, giving
brief descriptions of some of the more important
declarations used by the program. Figure 5-5 out-
lines the procedures and functions used by Adven-
ture 1. Again, there are brief comments regarding
some of the more important of these. Figure 5-6
19
BEGIN
Procedure invocations (once only) at the
start of the game play.
introduction;
initialize;
REPEAT
CASE location of
start: pstart;
grandroom: pgrandroom;
Fig. 5-7. The code outline for Adventure 1:
the Main program block.
flames: pflames;
END;
UNTIL quit OR done;
Main control loop of the adventure game program.
Various location procedures are repeately invoked
until the game is complete. This is signaled by one of
the two Boolean variables quit or done becoming TRUE.
congratulations;
Procedure called to wrap up the game and summarize
the results including the score for the player.
outlines one of the longer procedures of the pro-
gram, pmaze. This procedure is notable for having
many local procedures and functions. Finally, Fig.
5-7 outlines the main program block of Adventure 1.
Figure 5-8 is the dynamic structure diagram of
Adventure 1. It shows the relationships between
various groupings of procedures and functions used
by the program.
CHAPTER PREVIEWS
Chapter 7 is entitled “Representing the Map.”
It discusses the use of enumerated types in Pascal
programs generally an in adventure games specifi-
cally. You should pay special attention to this brief
chapter. It lays groundwork for a technique that is
used repeatedly in later adventures. At the end of
Chapter 7, the map of Adventure 1 is presented in
the graphic format explained in Chapter 2.
20
Chapter 8 is entitled “Controlling the Play”
and deals with the use of enumerated types in con-
junction with Pascal case statements. Chapters 7
and 8 go hand in hand. Go back and reread both
chapters after you finish them the first time. The
use of enumerated types in Pascal case statements
is one of the most powerful Pascal coding tech-
niques. Unfortunately, it is also one of the most
underused. Master the technique, and you will have
a valuable tool for all your Pascal coding, not just
your adventure game writing.
Chapter 9 is entitled “Mazes in the Middle”
and deals with the implementation of the maze in
Adventure 1. It discusses the use of local proce-
dures and functions as well as local declarations in
general.
Chapter 10, entitled “Other Techniques Used
in Adventure 1” discusses what I have termed the
ad hoc code of Adventure 1. It explains how mis-
congratulations
oe
eae
Conver) Come)
SIciIcIo
ogrereaction
=
(em)
a7
MRT ee Bet eS
pm11 pm12 pm13 pm14 pm15
\wY WN” owas a
pm16 pm17 pm18 pm19
A / =" _
Fig. 5-8. The structure diagram for Adventure 1.
cellaneous features of the game were implemented problem in a very specific way and may not be as
by direct Pascal coding techniques. The techniques generally applicable as some of the techniques de-
are ad hoc in the sense that they solve a specific scribed in earlier chapters.
22
Pascal Adventure 1
This chapter presents the complete listing of the
first Pascal adventure game. This example is meant
more to illustrate features of Pascal than to be a
serious game. To get the most out of this program,
you should go through the following steps:
1. Skim read the program right now. Try to grasp
the overall structure of the program and don’t
worry too much about the fine details. Refer
back to the code outlines in Chapter 5 as you
skim. Jot down any questions you may have
about the overall organization of the program.
2. Read and study Chapters 7 through 10, which
discuss the details of the Pascal coding of this
adventure. As you read these chapters, study
LISTING 6-1. PASCAL MINIADVENTURE
4,
Before typing, see page 143, “The #*%%!!? ESCAPE Key”
the listing of Adventure 1 and absorb the details.
Imagine how you will apply the language fea-
tures discussed to your own adventure game
programs.
. When you have finished Chapter 10, go back and
reread the listing of Adventure 1 in detail. Check
your list of questions from step 1, and see how
many of them have now been answered.
Write an adventure of your own using the
techniques illustrated by Adventure 1. Make up
your own map and your own location descrip-
tions. Notice what parts of the Pascal code you
can use almost unchanged and what parts you
have to alter radically.
(in addition to Chapter 4, page 12.)
COCO OIC CK OK OOK OOOO CICK GIO GK GK)
(x *)
xX a dv @ nm t wor «& # 4 x)
(xX x)
(kK This is an example of the Pascal language x)
23
(*k features that are useful in writing advent~- *)
(kX games. It is not really a serious game, but x)
(k more an instructional exercise. *)
(x x)
COO OOOO OK GOK OK KK KKK KKK )
CXHo+k) ——e Compiler “swap” instruction, see Chapter 17, page 141
FROGRAM miniadventure;
CONST
fF = 12s
ew = 12;
nw = os
ne = ben
Sew = 14;
nonly = 1:
nsew = Les
newud = O14
Sw = 10;
ns = 8
danly = S24
cin = aan §
ud = 48;
SU = 18s
TYFE
rooms = (start, grandroom, vestibule, narroawl,
lakeshore, island, brink, iceroom,
ogreroom, narrow2, pit, crystal,
batscave, steam, deadend, ladder,
maze, flames);
Girections = (MaSaG@uWatta dd) §
byte = O,.2553
VAR
commands STRING;
(xk holds user typed direction *)
chs CHAR:
dchars: SET OF CHAR:
(* characters which correspond TQ the
24
acceptable initial letters OF
direction commands. initialized TQ
ae 2 aa —- e — ' *)
location: rooms s§
ogreloacs: rooms;
visited: ARRAY C start..flamesJ QF BOOLEAN;
nexts directions;
twopow: ARRAY EO n..dJ OF INTEGER;
turns: INTEGERS
done: BOOLEANS;
quits BOOLEAN;
eaten: ROOLEANS
awake: BOOLEANS
readmsg: BOOLEAN:
carrying: BROOLEANS
dropped: BOGLEANS
trapped: BOOLEANS;
cooked: BOOLEAN:
COOK KOK KKK)
(kK wip © x)
CK KKK KKK KKK KD
FROCEDURE wipe;
REGIN
write (chr (ff));
END;
CKPiminii.text*) These are “include” instructions; see Chapter 17, page 140 for more info.
(XPiminiz.textxk) As written, they assume all files are on Drive 1 (Volume #4: in Pascal.)
(Xtimazel.textx) For this and for page 81, refer to “A note about disk Drive numbers in file
(XGimini2. text) names and commands” on page 3 of this PDF.
OK OK A new file named “mini1.text” begins here
x)
COOK KOKO KKK KKK KK KK RK KKK KK KKK AK KKK KK)
(x i fi¢¢ Fr © d@wteée«t i oO on *k)
OK KKK KKK KKK KKK KKK KKK KKK KK KKK KKK KKK KKK)
FROCEDURE introduction;
BEGIN
wipe; (kK clear screen *);
writeln (*Welcome to miniadventure!’);
25
writeln
writeln
writeln
writeln
writeln
writeln
writeln
writeln
writelny;
writeln
writeln
writeln
writeln
writelns
writeln
writeln
writeln
writeln
writeln
writeln
writelns
writeln
(? Your goal will be to find a treasure’);
(?and bring it to the starting point.’);
(7You will also get points for finding’);
(?each location in the adventure.’ );
(*Points will be deducted for various’);
(7undesirable happenings: waking the’);
(ogre, getting eaten, getting toasted,’);
Cfetc.")3
(*I will guide you and be your eyes and’);
(ears. I will describe your location’);
(‘and give you special instructions’);
("from time to time.*)¢:
(*To command me, tell me a direction’);
(*ta take north, south, east,.");
C(*west, Up, ar down.")s
(?note: I only look at the first letter")y;
("of the command, so abbreviations’);
("are acceptable.’)s
(* When you are ready to begin your’);
writeln| (adventure, just press "return". 7)3
use “write” not “writeln” (writeln pushes top line of text off the screen)
readln(cammand) ;
FE.ND
wipes
(xk FROCEDURE introduction *)5;
OOK ROKK KR KKK KK KK KKK KK KKK KKK KK KKK)
(x
i n
i t i a |] i z e x)
COO OOK KKK KKK KKK KK KKK KKK KKK KK KKK)
FROCEDURE initialize;
VAR
loc:
BEGIN
26
location
dchars
done
quit
cooked
eaten
awake
rooms;
starts
Eq? 4?
false;
false;
false:
false;
false;
readmsqg := false;
carrying := false;
trapped := false;
dropped := false;
turns = Oy
twopowEinI]:= 1;
twopowls]:= 2;
twopowlel:= 43
twopowlLwJ:= 8;
twopowlud: =16;
twopowld]:=32;
FOR loc := start TO flames DO
visitedClac] := false
(kK enddo *);
END (x PROCEDURE initialize k);
OX OK KKK KKK KKK KKK KKK KKK KKK KK KKK KK KKK KKK)
(x S Cc oO r e x)
COKKOOK KKK KKK KK RK KK KR KKK KK KKK KKK KKK KKK KK)
FUNCTION score: INTEGER;
VAR
lac: rooms s
Sc: INTEGERS;
BEGIN
tart TO flames DO
sc r= sc + 10
(x endif *)
(kK endda X);
IF NOT quit
THEN
Sc s= sc + BOs;
IF cooked
THEN
sc
27
ScCOre := sce
END (* FUNCTION score X);
CORR OOK ROKK KK KKK KK )
(K congratulatians x)
CORO OKO KK ROKK KKK KK KOK KK KK)
PROCEDURE congratulations;
BEGIN
IF NOT cooked
THEN
REGIN
IF NOT quit
THEN
REGIN
Capitalize as ‘Congratulations’ or ‘CONGRATULATIONS’ if you like
wreitelnm (7 XXKKK congratulations XkKKK")3;
writelns
writeln (*You got the treasure out in only")
writeln (turns:4,"* turns. *)3
END;
writeln (*You scored’, score:4, * points out’)
writeln (* of a maximum of 2OO points.")3
writeln ("Sa lang for now, came again saon!"):
END
ELSE
writeiln (Sorry about that - try again soan'!")
(kK endif xk);
readin (command);
Wipes
END (x FROCEDURE congratulations *):
28
ORK KKK KR KK KKK KR KK KKK KK KKK KKK KKK KKK KKKKK)
(x w A tft cc AH Ww aiy x)
CROCK KKK OK KOK OK KOK OK KOK KK)
FUNCTION whichway:directions;
BEGIN
turns = turns + 1;
REF EAT
REPEAT
writelns
write ("Which way?===%");
readin (command) ;
UNTIL length (command) + O;
ch := command(eid;
CASE ch OF
*n's whichway = 3
7s" 5 whichway := 3
"e*s whichway : = @3
"wk whichway #= wy
a a whichway #= us
*d*s whichway = ds
7 qr quit r= trues
ENDs
UNTIL ch IN dcharss
writelns;
end (xk function whichway *)43
COCO OK KKK KKOKKK KK KKK KK XK)
(x rm oO w a y x)
COCO OOOO OOK OK ORK KKK KOK OK KOK Ko)
REGIN
29
writelnys
writeln (Yau cannot go in that directian.*)3;
END s
(K(x figs ”
Bs New file named “mini3.text
Cte Sees eesesceeceseese ee eseseeeess 26 2.6.8 2)
Cx Oo g Yr @€@ a c t Gd @ FT x)
CGO OOOO IC GIGS IGG II KE
FROCEDURE ogqreaction;
REGIN
IF NOT awake
THEN
BEGIN
writein ("This is the ogre’’s lair!’);
weiteln (If you are not careful, you" "11")3
writeln ("wake him.")s;
IF (turns MOD 7)=0
THEN
REGIN
awake = true;
wreiteln ("Now you’ *ve done it!");
writeln ("You woke the agre - better’);
writeln (get out of here while you can*)3;
END (xk IF X)5
END
ELSE
REGIN
writeln ("You wouldn’*t listen ta me would’);
writeln ("your You really better get out’);
wreiteln (‘af here before you get eaten! ")s;
IF carrying
THEN
IF (turns MOD 2)=0
THEN
BEGIN
30
writeln (€* Tao bad! ! The agre caught yau");
writeln (and roasted you for dinner.*)s;
writeln ("Better luck next time!!");
eaten := trues
quit m= trues
END
BSE
REGIN
wreiteln ("Get cut fast if you dan**t want");
wreiteln (*to be a big-mac for the ogre! !");
END
ELSE
IF (turns MOD S) =o
THEN
BREGIN
weiteln (* Too bad - you’ "ve been eaten! *)s
eaten : t.
quit os= true;
END
(k endif x)
END (xk IF NOT awake k),
END (x FROCEDURE ogreaction );
CCK KOKO OK OK OK OK KKK OK KK KKK)
(x Pp S t a r t x)
CK ORK OK KKK KKK KK KKK KK KKK KK RK KK KKK KKK KKK)
PROCEDURE pstarts:
REGIN
IF carrying
THEN ‘
done 3: true
ELSE
BEGIN
31
writelin ("You are standing by a hole in");
wreiteln ("the ground. It looks big enough") s
writeln (*ta climb down."*);
CASE whichway (GF
My Sa @aWe noways
ur writeln ("You can’ *t jump to the clouds!") 5
ds: location := vestibule;
END s
END (xX IF carrying *)5
END (x FROCEDURE pstart *);
COCO KOKO KK KOK KOKO KOK OK)
(x Pp vy e s t i b u 1 e x)
COOK O OKO K KOK OK OK KKK OK )
FROCEDURE pvestibule;
REGIN
Use proper capitalization in all of the descriptions throughout this game:
32
writeln ("you are in the entrance ta a cave’):
writeln (*of passageways. there are halls");
writeln (‘leading off ta the north, south," );
writeln ("and e@ast. above you is a tunnel’);
writeln ("leading to the surface.*);
IF dropped
THEN
REGIN
writeln (* Ta the north, through a narrow crack,’
writeln ("You can see the treasure. If you?) s
writeln (stretch your arm through you might*);
writeln ("reach it. Do you want to try?")s3;
readin (command) gs
IF (command = “yes*) OR (command = "y*)
THEN
BEGIN
carrying : = trues;
dropped := false;
END (xk IF xX),
ENDs
CASE whichway OF
ne location := nmarrowl;
Sy locatian := grandraams
es lacation := iceraoms;
wed: moways
us location := starts
END (x CASE whichway X)5;
END (x FROCEDURE pvestibule *);5
OOO ORK OOK KK OOK KOK KKK OK KOK KK )
(x p aq roeaondreoaoo i m *)
COOK OOK OOK OK KK KK )
FROCEDURE parandroam;
REGIN
writeln (You are in a huge open roam, with");
writeln (?an immense expanse of ceiling.*);
writeln (7A dark passage leads west and a*)3
writeln (*?narrow crawl leads downward." )3
CASE whichway OF
Wi location #= brinks
d: location := iceroam;
Tig Sy Gate noways
END;
END (xk FROCEDURE pgrandraom *);
OOOO OK KKK KKK KK KOK KKK KK KK KK KX)
Cx pon aeerreow fi x)
COOK OOK KOKO KK KOKORO KK KK KKK KK KK)
FROCEDURE pnarrowl;
BEGIN
writeln ("You are in a narrow passage which") 3
33
writeln ("continues to the north. It is");
writeln ("extremely narrow to the south.’)3
writeln (* A very tight crawl also leads east.”*)
writeln (°A curious odar seeps through it.")4
writeln (7 I would think twice before trying’)s;
writeln (*to go that way!*)5
IF carrying
THEN
REGIN
writeln ("The treasure won"*t fit through”);
*
8
writeln ("the crack going south. Do you want");
writeln (*to leave it here?*’);
readin (command) ;
IF (command = “*“yes*) OR
(cammand = “y")
THEN
BEGIN
dropped := trues:
carrying := false;
END (kx IF x);
END (x IF carrying Xk);
CASE whichway OF
akeshore;
mis location 1
ogreraoms:
@s location :
Ss: writeln (?
Walla ds noways
END (x CASE whichway *);
END (x PROCEDURE pnarrowl *) 3
Ct Peseseses se seseetse ses Fes F353 25393 2
(x p tl ak es RH oF Ff 6 *)
COO OOO OR KIO KOK OK OK KKK KK )
PROCEDURE plakeshore;
34
It*’s too narrow to get through!’*
)
®
a
BEGIN
writeln ("You are on the shore of a vast*);
writeln (‘underground lake. Narrow passages");
writeln (’wind away to the east and south. *);
writeln ("A small island is visible in the’);
writeln (center of the lake to the north.’);
CASE whichway OF
nz: location := island;
85 location := narrowly;
@s location := narrow2;
w.u,ds noaways
ENDs
END (x PROCEDURE plakeshore *)3;
CROOK KKK KK KKK KKK KKK KK KK KX)
(x p is 1 aon id x)
CORK OK KKK KKK KKK KKK KKK KKK KKK KK KKK KKK KKK)
PROCEDURE pisland;
BEGIN
writeln ("You are on a small island in the’*)s
writeln ("center of a huge underground lake.*) 3
writeln (*Dark frigid waters surround you an"),
writeln ("’all sides. You can barely make out*)
writeln ("the shoreline to the south.");
weiteln (*A small message is scratched in the*)
writeln ("dirt here. It says: “*The treasure’)
writeln (?may be found in the maze.**")3
CASE whichway OF
My GaWa lia di noways
Ss
ENDs
readmsg
location := lakeshore;
:= true;
END (x FROCEDURE pisland *);
«
"
35
COOK GOK KK KOK KOK KK KK)
Cx
b r i n k x)
(KKK OK OK OK KOK KKK OK KK KK KK KKK KKK KK KK)
FROCEDURE pbrinks
BEGIN
END
writeln
writeln
writeln
writeln
writeln
writeln
writeln
writeln
writeln
writeln
You are on the brink of a steep");
incline. The bottom of the pit’);
is over fifty feet below you. ")s;
You could probably slide down’);
(safely, but I won’*t promise you");
(that you could get back up.*);
(* To the west is a dark opening’
(into a rubble~filled tunnel. A’
("vampire bat just flew out of it’
(* shrieking." ) 3
¢*
¢*
(*
¢*
?
yg
ds
3
CASE whichway OF
MN, Se Gaus
we
ds
END;
(k PROCEDURE pbrink
noway
location := ogreroaom:
lacation #= pits
K)s
CK KKK KKK RK KKK KKK KKK KKK KK KKK KK KKK KK KKK)
(x
A
i
c oe r o oO Mm x)
CK KK KK KKK KKK KKK KKK KKK KKK KKK KKK KKK KE KKK)
FROCEDURE piceroam;
REGIN
36
writeln
writeln
writeln
writeln
wreiteln
writeln
writeln
writelin
writeln
(*You are in a room whose walls are’*)s:
("formed from a deep blue crystalline”);
(7ice. To the north a narrow tunnel"):
Copens. From the other end of the tunnel’);
Can aminous growling sound may be");
(heard. To the east a sparkling *)s
Cluminescence emanates from a broaad*)s
(7 apening. To the west a passage");
(‘leads back toa the vestibule.” ),
CASE whichway OF
es lecation := crystals:
ms lacation := oagrerocoms
we location := vestibule;
Salt,ds noway 3
END;
END (x PROCEDURE piceroom *)3;
COOK KK KOK OK KKK KK KK KK KKK KKK KK KK RK KK KK)
(x Q g r e r QO Q m x)
COOK OKO RKOK OK KK KKK KK KK KK KKK KKK KK)
FROCEDURE pogreroom:
)
VAR
i,j: INTEGERS:
FROCEDURE generaldescriptions:
BEGIN
writeln (* You are in a low room whose walls’);
writeln (*’are covered with ominous dark’);
writeln (*gouts of dried blood. The center’);
writeln (of the room is dominated by a’);
writeln (*firepit, which contains burned’);
writeln (“out coals and a lang spit suspend-*)
wreiteln ("ed aver its center." );
writeln ¢? Fram one dark carner emanates a*);
writeln ("“harrible growling noise like that *)
writeln (of some unspeakable manster snoring’
writeln (during its dream of rending you limb
wreiteln (fram limb and making you its dinner!’
END (xX FROCEDURE generaldescriptian *);
BEGIN
generaldescriptioan;
ogreactions
IF NOT eaten
THEN
BEGIN
writeln
(*There are exits to the east,
west, *)
)
)
37
New file named “maze1.text”
writeln C north, and south. 7);
CASE whichway OF
we lecatian := narrowls
es location := batscave;
mi location := narrow2;
Ss location := iceraom;
ds BEGIN
quit = trues
eaten := trues;
writeln (*Oh no!! You dummy!!!" ) 5
writeln (You just fell in the firepit’):
writeln (7and made such a ruckus that’);
wreiteln (you woke the agre. IT hate ta’);
writeln (*tell you this, but you are");
wreiteln ("also trapped!*);
FOR i = 1 TO 2 DO
BEGIN
FOR j s= 1 TO 1900 DO;
weite d?.7)¢8
END (xk DO x);
writeln (*? You have heen added to the");
writeiln Coagre**s gourmet recipe library! *)3
writeln ("Better luck next time.")3
ENDs
ue Noway §
END (* CASE whichway *);
END (x IF NOT eaten *)35
END (x PROCEDURE pogreroam ) 4
Ck CX
x)
COOK OK KOK KKK KOK KKK KOK OK )
(x a m a = e x)
COCO KICK KK KOK OK KKK KKK)
FROCEDURE pmaze;
TYPE
Mazerooams = (mi,m2,m3,m4,m5,m6,m7,m8,
38
m9,miO,milt,mi2Z,mis,mi4,
miS,mil6,m17,m18,m19)
VAR
mazelacs MAZeraams s
bitset: ARRAYE directions] OF BOOLEAN;
FUNCTION bittest
(ve INTEGER; dir: directions): BOOLEAN;
BEGIN
IF (¢(v DIV twopowldird) MOD 2)=1
THEN
bittest := true
ELSE
bittest := false
(kX endif *)3
END (* FUNCTION bittest x);
FROCEDURE describe(wh: INTEGER);
VAR
dirs directions;
BEGIN
writeln (You are in a maze of featureless");
writeln ("*passaqges. There are exits visible’);
writeln (“in the following directions:’);
IF bittest (wh,.n) THEN write (*n
IF bittest (wh,s) THEN write (*s
IF bittest (wh,e@) THEN write (’e
IF bittest (wh,w) THEN write (*w 73
IF bittest (wh,u) THEN write ("uu
IF bittest (wh,d) THEN write (*d
writeln;
end (* procedure describe *);
FROCEDURE sameplaces
BEGIN
writeln ¢* You have crawled around same*)
writeln (*twisted tunnels and wound up*)
“2 Es
39
writeln (where you beqan.*)3:
END (xk PROCEDURE sameplace *)>3
PROCEDURE treasure;
REGIN
IF NOT carrying
THEN
REGIN
IF readmsg
THEN
RE(GIN
writeln (*The treasure is here!!*);5
writeln (*Do you want to take it now?’);
readin (command) ;
IF (command = *yes*) OR
(command = “y"*)
THEN
carrying #= true
(k endif xk);
END
ELSE
BEGIN
writein (*The light is extremely dim here’);
writeln ¢(* You better get out or risk’);
writeln (*falling into a pit.")3
END (* IF readmsq *);
END (* IF NOT carrying ¥)5
END (X* PROCEDURE treasure *)3
PROCEDURE pml;
BEGIN
writeln (*You are in a maze of featureless"):
writeln (*passages.")3
writeln (*from here you can go south, east,*);
40
writeln (* west, or up.")3
CASE whichway OF
Sy location := ladder;
@s mazeloc := ms
we mazelon s= m4;
us location := steam:
nads noway s
END;
END (x pmil kK);
FROCEDURE pms
BEGIN
describe (nw) s
CASE whichway OF
ns mazeloc := mi;
ws sameplace;
G2, Satt.ds noways
END s
END (x PROCEDURE pm2 x);
FROCEDURE pind;
REGIN
describe(ne)s
CASE whichway OF
ns mazeloc := mil;
e: sameplace;
SaWalled? NOWAYS
END;
END (x FROCEDURE pms *);
PROCEDURE pm4;
REGIN
describe (sew) ;
CASE whichway OF
Ss mazeloc := m7;5
e: mazeloc := m3;
ws mazeloc := mS;
Malt, ds noway §
ENDs
END (xk FROCEDURE pm4 x);
FROCEDURE pmSs
REGIN
describe(nanly);
CASE whichway OF
ns mazeloc := m1;
BuWy Sally de noway§
END:
END (x FROCEDURE pms *)3
FROCEDURE pmés
REGIN
describe(ne)s;
CASE whichway OF
ns mazeloc := m4;
es sameplace;
SaWalta ds noway §
END;
END (kx PROCEDURE pmé& *)¢;
PROCEDURE pm73;
BEGIN
42
describe (nsew) :
CASE whichway OF
ns mazeloc s= mdz,
Ss mazeloc := m9;
e: mazeloc := més
we mazeloc := m6;
unde noways
END;
END (kx FROCEDURE pm7 k)3;
PROCEDURE pm&s;
REGIN
describe (nw) 3
CASE whichway OF
ne mazeloc := mos
Ws sameplaces
2,Satt.ds noways
END:
END (k FROCEDURE pm x);
FROCEDURE pm?;
BEGIN
describe(sw);
CASE whichway OF
Ss: mazeloc +:
we mazeloc :
NaG@alta ds noways;
END;
END (x FROCEDURE pm? *);
PROCEDURE pmids
BEGIN
describe (ns):
CASE whichway OF
ms mazeloc := m8;
Ss sameplace;
SaWelladt NOQWAYS
END;
END (x PROCEDURE pmiO x),
FROCEDURE pmii;
BEGIN
describe (newud) 3
CASE whichway OF
nme mazelac s= m9;
er mazeloc := més;
we mazeloc := miOs
ue mazeloc s= mi;
d: mazeloc = mizs
: noways
END;
END (kX FROCEDURE pmil *);
PROCEDURE pmiis
BEGIN
describe (dn) s
CASE whichway OF
ms mazeloc = mis;
ds: mazeloc := mié;
@x,SaWall? MOWAYysS
44
END;
END (XX PROCEDURE pmi2
FROCEDURE pmi3;
BEGIN
describe dn) ;
CASE whichway OF
ne mazeloc
ds mazeloc
2.S.W,lls maways
END:
END (x FROCEDURE pmis
FROCEDURE pmi4;
BEGIN
describe(dn) s
CASE whichway OF
ris mazeloc
ds: mazelac
Gq SaWa ltt NOWAY &
END;
END (* FROCEDURE pmi4
FROCEDURE pmiSs
BEGIN
describe (ud) 5
CASE whichway OF
us mazelac
: mazelac
NaSaeGaWt NOWays
ii
3 3
ee
ai “28
me
ENDs
END (* FROCEDURE pmiS *)3
FROCEDURE pmildés
BEGIN
describet(ns) ;
CASE whichway OF
ns mazelac := m1l7;
Ss sameplace;
@uWalla dt Nowey §
END;
END (xk FROCEDURE pmié& &)s
FROCEDURE pmi7s
BEGIN
describei(ns)s;
CASE whichway OF
ns mazeloc = mi;
5: mazeloc := mild;
@aWatts ds noway 3
END s
END (kx PROCEDURE pmi7 *);
FROCEDURE pmi®s
REGIN
describe (ns) ¢
CASE whichway OF
ms Mazeloc
Ss mazeloc
I a naoways
46
END;
END (x FROCEDURE pmi8 *);
FROCEDURE pmi?;
BEGIN
describe (su)
treasure;
CASE whichway OF
S mazeloc s= mig;
ues mazeloc := mids
Me @aWadi noway §
END;
END (x PROCEDURE pmil9 *):
BEGIN (xk FROCEDURE pmaze *);
mazeloc := mi;
REPEAT
CASE mazeloc OF
mis pmis
Mie k pm2y
mas pmss
m4 s pm4s
mos pms;
Mos pmeas
m? s pms
mes pms sy
ms pm? 3
miG: pmo;
mids pmil;
mics pmizy
mic3s pmiss
mids pmi4;
mis pmics
mig: pmlé;
mis pmi7;
47
mi8: pmigy;
miQs pm1l?;
END (x CASE mazeloc );
UNTIL location <=? maze;
END (k PROCEDURE pmaze *) 3
(CK OX a ”
“) New file named “mini2.text
OX K KK KKK KKK KK KK KKK KK KKK KK KKK KKK KR KKK KEK)
(x Pp on aA F F @ W 2 x)
CKO KOK KKK KK KK KK KKK KKK KOK KKK KKK KKK KKK)
FROCEDURE pnarroaw2;
BEGIN
writeln ("You are in a very narrow passage." )3
writeln (*To the west the passage opens out");
writeln ("by a lake shore. Toa the east it is"):
writeln (even tighter. You just might be");
writeln (‘able to squeeze threaugh if you try"):
writeln ("real hard.*);
writeln ¢° There is also a strange looking" )s;
writeln ("alcove in the south wall that seems");
writeln (*toa open into a very dark tunnel.*)3;
CASE whichway OF
we location := lakeshore;
e: location := steam;
= location := ogreraoms:
Matha: noways
END;
END (xX PROCEDURE pnarrow2 *) 4
COO OOK KK ROKK KK OK KK KKK KK )
(x p p i t x)
COCO OOO OK KKK KKK KOK KOK KK KK KK )
PROCEDURE ppit;
BEGIN
48
writeln (You are at the bottom of a fifty"):
wreiteln ("foot pit. The walls are just a*)3:
writeln (*hair tao steep ta climb. The pit"):
wreiteln ("is empty except for a few ald");
writeln (dried bones ~ I can**t tell af they’)3
writeln Care human ar not! ! In the center")s
writeln ("of the pit is a nmarraw shinny’);
weiteln (leading downward." ) 3
CASE whichway OF
ds: location := ladders
us BEGIN
writeln ("If yau try ta climb that, *)3
writeln (Cyou’"“re sure to kill yourself!")s;
END;
Ty Sa @aWe noways
END;
END (X PROCEDURE ppit *);
CK KK KKK KKK KKK KKK KK KKK KR KKK KKK KK KKK KKK)
(x Pp c r y S$ t ail x)
COOK OK OK KKK KK KKK KKK KOK KK KKK KKK KKK)
PROCEDURE perystals
REGIN
writeln ("You are in a shining hall af crystal.’);
writeln ("It is intensely cold but also chill-*)3;
writeln (ingly beautiful. There are glass’);
writeln (*formations rising from the floor’)s;
writeln ("as if they had grown there, yet’);
writeln ("delicately sculptured with multi-*)4
writeln ("faceted sides. An intense white’);
writeln (light shines brilliantly from the’);
writeln (*floor, which is also made of a");
writeln (*mirror smooth lead crystal. The Light’ )s:
writeln ("is almost blinding and the many");
writeln (reflections that it sets off among”):
writeln ("the crystal formations of the room’);
writeln (*make it almost impossible to tell’),
writeln ("where the room begins and where’);
writeln ("it ends. *)3
CASE whichway OF
@s location := maze;
NaWs lecatian := ogreraam;
Salles noway §
END:
END (x FROCEDURE crystal xk);
COOK KK KK KKK KKK KK KK KKK KKK KKK)
(x p b a t S$ © A Vv @ x)
CK KK KKK KKK KKK KKK KKK KKK KKK KKK KKK KK KKK)
FROCEDURE pbatscaves;s
REGIN
writeln (*You are in a steep cavern filled’);
writeln ("with shrieking vampire bats.
writeln (* swoop and dive at you by the
writeln (* thousands. If I were you, I
writeln ("get out as quick as I could.
writeln (*are openings to the west and
CASE whichway OF
we location := ogreroom;
ns location := steam;
@u Salt, noway §
END;
END (x FROCEDURE pbhatscave *)3;
COOK OOK OKO ROK KK KK KK KKK KKK KK KX)
(x Pp S t e a m x)
COOK OK KK KKK KKK KKK KKK KKK KK KK KKK KKK)
PROCEDURE psteam;
BEGIN
50
They") 5s
sa 5
would’) s
There’);
north. ")¢
END
writeln
writeln (’a stifling steamy vapor. There are’);
writeln ("innumerable small geysers scattered’);
writeln (about, each contributing its own steam’);
writeln ("to the general mist.7);
writeln (*To the west is a dark opening, as"):
writeln ("well as to the north. Further out’);
writeln (in the middle of the room is a dark’):
writeln ("opening in the floor into which weiner 24
writeln ("af the vapor seems to be seeping’):
(*You have entered a hall filled with’);
CASE whichway OF
we location := narrow2:
ns location := de adend;
ds: location := maze;
Ss location := batscave;
@ytts noways
END;
(kK PROCEDURE psteam *)3;
CK KK KKK KKK KKK KK KKK KKK KKK RR KKK KKK KKK KKK)
(x Pp 1 aodqdqeor x)
COOK GK KKK KKK KKK KKK KK KK KX)
PROCEDURE pladders
BEGIN
writeln ("You are at the base of a huge ladder’);
writeln (reaching up out of sight.
writeln (extend up at least SOO feet,
It must") s
and it will’);
writeln (’take someone brave in heart to scale’);
writeln (it.
writeln ("lead north and down.’)s:
CASE whichway OF
ns lacation := maze;
d: location := flames:
us IF carrying
THEN
BEGIN
There are alsa passages which’);
51
writeln (*? You can’*"*t carry the treasure up the’);
writeln ("ladder - it**s much too heavy! ");
END
ELSE
location := vestibule
(xX endif *)s;
@,SaWE noaways
END:
END (xk PROCEDURE pladder *);
COOK KKK KKK KKK KKK KKK KKK KKK KK KKK KKK RK KKK)
(x p f 1 a m e S *)
CHK KKK KKK KKK KKK KK KKK KKK KKK KKK KKK KK KKK KK)
PROCEDURE pflames;3
BEGIN
weiteln (Unfortunately you have fallen into’)s;
writeln ("an underground fire pit. It is the");
writeln ("source of the heat that produces’);
writeln (’the geysers in the steam room. You") s
writeln ("*have been toasted to a crisp to put’);
writeln ("it politely." )5
END (x FROCEDURE pflames x);
CHR KKK KKK KKK KKK KKK KK KK KKK KKK KK KKK KKK KKK)
(x p d @ a d e n d x)
CHK KK KKK KKK KKK KK KKK KK KKK KKK KKK KKK KKK KKK)
PROCEDURE pdeadend;
BEGIN
writeln ("Dead end.*);
CASE whichway OF
Ss location := steam;
52
Na@aWylta cs
END;
END
BEGIN (k ===
noways
(xk PROCEDURE pdeadend *);
CHK KKK KKK KKK KK KK KKK KKK KKK KKK)
cE
adventure
{ses
x)
CKKK KK KKK KKK KKK KKK KKK KKK KKEKK)
introductions
initialize;
REPEAT
visitedLlocationg)
CASE location OF
starts:
grandroom:
vestibule:
narrowls:
lakeshore:
island:
brinks
iceroaoms
agreroom:
narraws:
pit:
crystal:
batscave:
steams
deadend:
ladders
MAZE
flames:
END (x CASE
UNTIL quit OR done;
cangratulations;
END.
= true;
pstarts
pgrandroom;
pvestibule;
pnarrowls
plakeshoare;
pisland;
pbrink;
piceroom;
pogreroom;
pnarraw2;
ppits
perystal;
pbhatscavey;
psteams;
pdeadend;
pladders
pmaze;
pflames;
lacation *);
53
Representing the
In this chapter, I begin to show you how to use the
Pascal language to write adventure programs. The
Apple version of UCSD Pascal has been used in all
the examples in this book. These programs should
run without major modifications on any system with
UCSD Pascal.
Chapter 6 has presented the listing of Adven-
ture 1 in Pascal. This adventure would not present
much of a challenge to a seasoned adventurer. In
fact, even beginners would probably find it boring.
My purpose was not to write a serious game; I
wanted to introduce you to some of the features of
Pascal that are useful in writing adventures. By
studying these techniques, you can learn to write
your own adventures in Pascal. I hope that you also
gain a deeper understanding of Pascal and become a
better Pascal programmer as a result.
REPRESENTING THE ADVENTURE MAP
Figure 7-1 presents most of the initial map of
Adventure 1. The notation for the map is explained
in more detail in Chapter 2. Notice the use of the
54
Map
circled question marks and circled numbers to indi-
cate the problems of the adventure. Figure 7-2
shows the map of the maze incorporated in Adven-
ture 1. Finally, Fig. 7-3 lists the problems by
number and gives explanations of each one.
There are 18 rooms shown on the map in Fig.
7-1, not counting all the individual maze locations. I
will represent these rooms in Pascal by using an
enumerated type:
TYPE
rooms = (start, grandroom, vestibule, narrow1,
lakeshore, island, brink,iceroom,
ogreroom,narrow2, pit,crystal,
batscave,steam,deadend, ladder,
maze,flames) ;
VAR
location: rooms;
The concept of an enumerated type deserves a bit of
discussion.
la
Ww
Ss
deadend
Fig. 7-1. The map of Adventure 1.
55
Fig. 7-2. The map of maze in Adventure 1.
56
You must bring the treasure
to start in order to win.
To guarantee a win, you must carry
the treasure to narrow1 drop it,
go around to the vestibule, and pull
the treasure through the crack.
You can get to the vestibule from the
iceroom, but you must carry the
treasure through the ogreroom, and
you could be eaten in the process.
You cannot carry the treasure up the
ladder—it’s too heavy! You must find
another way out while still carrying
the treasure.
You must find the treasure deep in the
maze. Before the treasure will be
visible to you, you must discover the
message on the island.
Fig. 7-3. The problems of Adventure 1.
ENUMERATED TYPES IN PASCAL
Programmers sometimes have difficulty com-
ing to grips with the concept and use of enumerated
types. The reason for this is a psychological one. A
large part of traditional programming, whether it be
in assembly language or in so-called “high-level”
languages such as FORTRAN or BASIC, has dealt
with the invention of representations. Programmers
have become used to this necessity. In Pascal, the
concept of enumerated type provides a facility
which removes the need to invent representations
in certain situations. Suddenly the very program-
ming language itself solves part of the traditional
representational problem. The programmer is not
used to this. The programmer is fixated on solving
all representational problems—indeed the pro-
grammer has come to love this aspect of program-
ming. When faced with a ready-made solution, the
circuits become overloaded.
To make this more concrete, suppose for the
moment that you were writing a miniadventure in
BASIC. You might use a variable to keep track of
which room or location the player happens to be in
at any given point in the game. Now BASIC can on-
ly manipulate two kinds of data—numbers and
strings. Therefore, you must invent a correspon-
dence between the set of rooms in your game and a
set of numbers. The numbers will be used inter-
57
nally to represent the rooms. You might choose
start = 1
vestibule = 2
grandroom = 3
and so on.
On the other hand, it would be equally valid to
use
start = 1001
vestibule = 1002
grandroom = 1003
and so on.
If the latter representation were chosen over
the former, there is a likelihood that the choice
would be based on some intended programming
trickery. That is, part of the BASIC program might
be written to “know” that numbers greater than
1000 represent rooms.
In general, unless you resort to coding tricks,
the specific numbers chosen to represent the rooms
are purely arbitrary. They only serve to distinguish
one location from another and at some level, to
identify which location is under scrutiny. The key
points about these numbers are:
@ They are explicity chosen by the programmer.
@ They represent an external concept (rooms) in an
internal form (numbers).
@ They must be correlated mentally by the pro-
grammer; that is, the details of the representa-
tion are a factor in the coding of the program.
Pascal’s enumerated type facility enables you
to set aside the problem of representation in this
case. The enumerated type given above effectively
creates a new kind of data that Pascal can manipu-
late, auser-defined type called rooms. The variable
location may be thought of as a variable that can
take on any roomas its value. The user may think of
rooms in terms of their names as chosen in the type
declaration. There is no need to choose a set of
numbers to correlate to the names: the Pascal com-
piler takes care of this for you. Since you really
don’t care what the specific numbers are (unless
58
you had planned on some clever trickery), it is
better to let the details be swept under the rug of
compilation. When you write your program, you can
think in terms of room names, not numbers!
Since long years of training have made most
programmers feel that we should be personally re-
sponsible for internal representations, it is difficult
to make our minds let go of that feeling of responsi-
bility. However, when we finally do let go, the
freedom to think in terms of the problem instead of
in terms of the computer is the result. Once you
become accustomed to it, this can lead to an enor-
mous feeling of power and relief.
If you read through Adventure 1, you will see
that assignment statements such as:
location := vestibule;
location := flames;
are commonplace. How much more suggestive
than:
location := 2;
location := 17;
Of course, the enumerated type rooms only
represents the collection of locations in the adven-
ture. It does not include information about the con-
nections between locations. Figure 7-1 includes all
the connections. This version of the adventure will
only accept commands that indicate a direction in
which to travel. In particular, it only recognizes six
different directions represented by the single let-
ters n, s, e, w, u, and d.
From any given location only certain direc-
tions lead to other locations. For example, from the
Crystal room you may only proceed e, w, or n.
There is no way to go u, d, or s. This information
must be encoded in some fashion and interpreted by
the adventure program in order for the game to be
played. This has been accomplished by two
techniques that I explain in the next chapter. In the
process, I shall reveal how the general play of the
game is controlled. Both the representation of the
adventure map and the control of the game hinge on
the use of the Pascal case statement.
Controlling the Play
In the last chapter, I discussed enumerated types in
Pascal. The power of enumerated types is greatly
enhanced by the use of the Pascal case statement.
There are several examples of case statements in
Adventure 1. They all are quite important in making
the game work. I am devoting this chapter to a
discussion of some of these statements.
CASE STATEMENTS IN PASCAL
The general form of a Pascal case statement
may be indicated as follows:
case E of
I1:
l2:
al;
a2;
In this general description, there are a number
of elements that require further explanation.
case expression E
E must be an expression that evaluates to one
of the elements of what in Pascal is referred to as a
scalar type. In particular, enumerated types are
examples of scalar types. In the context of adven-
ture games, E could be a variable representing a
value of type rooms. Or, it could be a call to a
function that returns a value from the type direc-
tions= (n,e,s,w,u,d); More on this below.
The idea of the case statement is to allow a
program to take different actions based on the par-
ticular values of an enumerated type. The elements
11,12, ...,ln of the general case statement repre-
sent the possible values of the enumerated type.
Actually each li may be a list of elements from an
enumerated type. The elements al,a2, ... ,an in
the general description represent the Pascal code
that is to be activated in response to the corre-
sponding value or list of values li.
59
In Adventure 1 there are two major uses of the
case statement. Both involve the flow of control
through the game program.
CONTROLLING THE ADVENTURE GAME
If adventure were real, the player would either
be in a location at a given time, or he would be
traveling between two locations. In the act of
traveling the player would leave one location for
another by heading off in a specific direction, such
as north, east, or down. Thus, in programming
adventure these ideas must be represented in some
way using Pascal code. Specifically, you must find
ways to represent:
@ The adventurer’s location.
@ The possible directions out of each location and
the destinations they represent.
@ The decision as to which direction to take.
m The changing of location from one room to
another.
The Adventurer’s Location
I have already discussed the use of an enumer-
ated type (which I called rooms) to represent the
possible locations. To store a specific value of the
rooms type, I invented a variable called location.
var
location: rooms;
At the beginning of the game, the location
variable is assigned the value start. This represents
the initial room or starting point of the adventurer.
At various places in the adventure game code, as-
signments will be made to this variable. Such as-
signments will be dictated by the adventurer’s
choice of direction.
To organize the game, a separate Pascal pro-
cedure has been written for each value of the rooms
type. These procedures have all been given names
that consist of the letter p for procedure, followed
by an identifier from the rooms type. For exam-
ple, there are pstart, pflames, pgrandroom,
pogreroom, and so on. Each of these procedures
handles the activities that may take place in the
60
corresponding location
The main program code consists largely of a
case statement with case expression of type rooms:
case location of
start: pstart;
grandroom: pgrandroom;
vestible: pvestibule;
narrow1: pnarrow1;
lakeshore: plakeshore;
island: pisland;
end;
The meaning of this statement is extremely
simple: When the player is in room x, activate the
procedure px, which handles activities in that
room. At any time during the game, one of these
procedures will be in execution. The execution of
that procedure corresponds to the adventurer actu-
ally being in that room in real life. The program’s
structure very closely reflects the way we imagine
a real life adventure would happen.
Look back at the main program listing in
Chapter 6 and notice that the case statement is
enclosed within a while statement. This is neces-
sary in order to ensure that the case statement is
repeated over and over again. Otherwise, the game
would cease after pstart was executed once.
The use of a case statement nested inside a
while statement is common in Pascal programming.
Remember it—you will probably use it often during
your Pascal programming career.
Changing Locations
Part of the activity in each room is deciding
which way to go next. The adventure map shows
that in a given location it is impossible to proceed in
all possible directions. Only a subset of the possible
directions represent actual paths in the imaginary
adventure world. The code must reflect this fact. In
Adventure 1, each location procedure contains
another case statement that handles the travel from
location to location. The case statement also en-
codes the possible directions that may be taken.
This is best explained by considering an example:
The following code is found in procedure
plakeshore:
case whichway of
n: location := island;
s: location := narrow1;
e: location := narrow2;
w,u,d: noway;
end;
The identifier whichway is the name of a function
that determines the direction the adventurer
wishes to take. This is described in more detail
below. The identifier noway is the name of a proce-
dure that simply prints the message:
“There is no way to go in that direction.”
on the game player’s display screen.
The operation of the case statement is once
again quite simple.
@ A direction is chosen by the adventurer. This is
accomplished by a call to whichway.
wlf the direction chosen represents a possible
avenue of travel, the new room is assigned to the
variable location in the appropriate section of
the case statement. This will cause a new loca-
tion procedure to be invoked on the next cycle of
the main case statement.
g If the direction chosen does not represent a pos-
sible route in the adventure map, the noway
procedure is called. The value of the variable
location does not change. This means that the
same location procedure will be invoked on the
next cycle. The adventurer gets another chance
to find a way out.
Deciding on a Direction
In this adventure, only very simple commands
are used; only one of the six directions Nn, S, e, W, U,
or d may be specified. The function whichway is
responsible for this interaction with the player. It
prompts by “asking”
Which way?===>
The player responds by keying in a “command.”
This may be any string of characters. The Pascal
code examines only the first character of the string:
command{1]
If this character is one of the letters n, S, e, W, U, or
d, whichway sets its return value to the corre-
sponding value from the enumerated type direction.
For example, if the adventurer types in never,
whichway will return n as the direction to try.
Protecting Against Empty Responses
There is one problem that the Pascal code in
whichway must solve. If the adventurer simply
pushes the RETURN key the variable command,
which is used to store the response, will contain an
empty string. The length of the empty string is, of
course, 0. The character to be assigned to ch using
command{1] is then nonexistent. If a statement in
the program attempts to access this character, the
Pascal run time system will complain with a cryptic
message. The adventurer will be yanked from
pleasant fantasy back to mundane computer reality.
You must avoid this if at all possible, and it turns out
to be quite simple to do so. The program simply
checks the length of command and refuses to touch
it until the length is greater than 0.
Changing Location
The act of changing location is represented by
a simple assignment statement giving a new value
to the variable location. If you now go back to the
listing again, you will find that such assignments are
scattered all over the case statements that begin
with
case whichway of
These assignments collectively determine the map
of the adventure. That is, the connections between
different locations is implicit in these assignments.
This last point is worth a minute’s reflection. If
you were writing adventure games in a language
61
like BASIC, you would probably encode the map as
an array of numbers. The array would probably have
two dimensions, with any pair of subscripts repre-
senting a pair of locations. The contents of the array
for each such pair would be yet another number.
This number would be a code representing one of
two things:
1. The direction to take to get to the room rep-
resented by the second subscript from the room
represented by the first subscript.
or
2. A number that means there is no connection
between the pair of locations in question.
In terms of Adventure 1, you could imagine an
62
array MAP[18,18]. The rooms start, grandroom,
vestibule, narrow1, and so on would be rep-
resented by the numbers 1 to 18. Then the pair of
subscripts 1,3 would represent start, vestibule; the
pair of subscripts 4,17 would represent narrow1,
flames; the pair of subscripts 5,9 would represent
lakeshore; ogreroom; and the pair of subscripts
5,6 would represent lakeshore, island.
The directions n,s,e,w,u,d, and noway might
be represented by the numbers 1,2,3,4,5,6, and—1.
Then MAP[1,3] would equal 6; MAP[4,17] would
equal—1; MAP[5,9] would equal—1; and MAP[5,6]
would equal 1.
I could have taken an approach like this in
Adventure 1. However, it was unnecessary to do
so, because the Pascal implementation I have cho-
sen was available and was so much easier to under-
stand and work with.
Mazes in the Middle
Adventure 1 has a maze in it. The original adven-
ture game had not one but two mazes in it. Many
subsequent adventures, especially those of the un-
derground or cave variety, also contain mazes.
The maze offers a good opportunity for intro-
ducing more of the structural features of Pascal.
The maze is conceptually like a single location. Yet
in practice it contains many individual locations.
While you are in the maze you are “trapped” in a
miniature adventure within an adventure.
The entire adventure in Pascal is implemented
by a program. The maze is implemented by a single
procedure, pmaze. By analogy, because the maze
is like a miniature adventure, pmaze must be like a
miniature program. Indeed as you shall see, proce-
dures can have almost all of the features that a
Pascal program can have.
Although pmaze is a single procedure that
controls play in the maze, it has its own local or
nested procedures and a nested function. Local pro-
cedures and functions are just like the procedures
and functions that contain them, as far as Pascal
syntax rules are concerned. However, they may
only be invoked from the procedure or function that
contains them or from other functions and proce-
dures that are also local in the same sense.
The pmaze procedure also contains a local
TYPE and VAR section. The types and variables
thus declared may only be used inside pmaze. They
are not “visible” outside pmaze; pmaze is referred
to as the scope of these declarations. The scope
includes any nested procedures or functions that do
not redeclare the types or variables in question.
The concept of scope is fundamental to Pascal
and other languages like it, which are referred to as
block structured languages. It is beyond the scope
(pun intended) of this chapter to go into a full dis-
cussion. For full detail on this subject, consult your
authoritative Pascal language textbook. However,
by imitating these program layouts you should not
run afoul of the scope rules.
Local or nested declarations, including proce-
dures and functions, are provided in the language to
allow for better program structure. If a variable
logically belongs only to a certain procedure, it is
best to declare it there and there only. Then it
63
cannot interfere with other variables that have been
declared in the outermost part of the program. A
good example of this (in general) is a loop control
variable for a counting loop. Such a variable should
be a VAR declared locally within the procedure or
function that uses it. In some implementations
UCSD Pascal allows you to violate this guideline.
The International Standards Organization (ISO)
standard for Pascal does not.
Local procedures and functions are useful for
breaking down the code of a large procedure or
function. Good program design requires that each
procedure perform a single logical purpose. The
name of the procedure should suggest that purpose
inasimple way. pmaze is a good example, although
the name is somewhat cryptic. It would have been
more understandable if I could have called it some-
thing like handle__the__play__while__the
__adventurer__is__in__the__maze, but UCSD
Pascal limits the significance of an identifier to its
first eight characters. So if I ever named another
procedure with a long name beginning with
handle__the play — while __ the __ player
_is__in_. .. , I would have a name conflict. In any
case, the procedure pmaze handles a single pur-
pose that is easy to state and to comprehend.
Nevertheless, it is useful to be able to write proce-
dures and functions that are subordinate to pmaze
and that help pmaze carry out parts of its task. I
shall discuss these subordinate functions and pro-
cedures later.
LOCAL DECLARATIONS
The procedure pmaze declares an enumer-
ated type called mazerooms. The identifiers in this
type represent the individual maze locations. It is
used in a fashion analogous to the rooms enumer-
ated type in the main program. The maze rooms
are not given individual names, nor are they given
individual descriptions. Thus, I just call them m1,
m2, m3, and so on.
There is a single local variable declared in
pmaze—mazeloc. It is of the type mazerooms.
Its value represents the current location of the
player in the maze. The value is always one of the
elements of the local enumerated type maze-
64
rooms. The variable mazeloc is entirely analogous
to the global variable location.
I could have implemented the maze by simply
extending the global type rooms to include the
maze locations and using the variable location to
keep track of them as well as of the other locations.
In fact, this is what I do in the next adventure later
on. For now, I have chosen the local approach to
illustrate a point about Pascal. I shall let you decide
which implementation of the maze you like better
when you have seen both.
LOCAL PROCEDURES AND FUNCTIONS
The pmaze procedure has a large number of
local procedures and a local function. The proce-
dures fall into two classes—“location” procedures
and “support” procedures. The support procedures
carry out general tasks. They may be invoked from
several places within the overall pmaze code, or as
in the case of treasure, they may simply serve to
make the program more understandable.
The support procedures are describe, same-
place, and treasure. They perform functions that
are suggested by their names. The describe proce-
dure is used to print the general description dis-
played when the player visits any of the maze loca-
tions. The sameplace procedure is used when a
move by the player results in landing in the same
maze location as before the move was made. The
treasure procedure handles the discovery of the
treasure that is located in one of the maze rooms. In
addition, describe tells the player in which direc-
tions it is possible to continue from any given maze
location. This makes it easier to map the maze.
The location procedures of pmaze are pm1
pm2, . . ..pm19. Each of these procedures corre-
sponds to one maze location. They are analogous to
the location procedures of the entire adventure
(pmaze itself is one of these). However, pm1
through pm19 handle only the maze locations,
which are sublocations within the maze. All of these
procedures perform the following duties:
gw Print a description by calling describe.
w@ Determine the direction of travel desired by the
player, by calling whichway.
@ Determine the resulting maze location or adven-
ture location after the player’s move is carried
out. This is accomplished in each procedure by
using a case statement as discussed in the previ-
ous chapter.
The location procedure pm1 does a little ex-
tra. In response to a player’s directions, it allows
not only mazeloc but also location to change. This
is the way the player gets out of the maze—by
returning to pm1 and then giving a direction that
goes out of the maze.
The only local function in pmaze is bittest. It is
an example of poor Pascal coding practice. It is
included because I wrote it that way originally, and
because it illustrates the kind of thinking habits that
FORTRAN and assembly-language programmers
fall into. It uses so-called “bit-fiddling” techniques
that are better represented in Pascal using set vari-
ables.
65
Other Techniques Used in Adventure 1
In this book, I make every attempt to implement the
adventures using “general” code. That is I want
procedures and functions to serve as many parts of
the adventure as possible. I also want to write
procedures and functions that may be used as pat-
terns. Some, like whichway, can be used directly in
other adventures. Others like the location proce-
dures provide a skeleton or template. The same gen-
eral approach may be used in other adventures to
accomplish the same goals.
There are many times, however, when you
just have to give up on generality. There are cases
in which you need fairly elaborate code in order to
implement some feature of the game, and that code
cannot be used for anything but that single feature.
There are examples of this kind of code in Adven-
ture 1.
The location procedure pstart, pvestibule,
pnarrow1, pisland, pogreroom, ppit, pladder, and
pflames each contain one or more sections of spe-
cial code. Let’s just go through them to get a flavor
of the kind of things that are possible.
66
The pisland Procedure
This procedure provides the simplest possible
example. The procedure pisland contains the
statement
readmsg: = TRUE;
What this represents in the game is the fact that the
adventurer has read the message to be found on the
island. This is important later on—in the maze—
because the treasure cannot be found until the mes-
sage has been read. This is an extremely simple
example of solving a problem. In this case, the
adventurer solves the problem just by visiting the
island. Later, when you add commands to your
adventures, you might require the adventurer to
explicitly command the guide to READ MESSAGE
or something like that.
There is a general principle in this example, as
well. The variable readmsg is of the Boolean type.
This means that its value may either be true or
false. Such variables are useful for representing
events in adventure games. More precisely, a
Boolean variable may be used to record the occur-
rence of an event. In this example, the event was
the reading of the message. As long as readmsg has
the value false, the event has not occured. When
and if the value becomes true, the event has oc-
curred.
The pflames Procedure
This procedure is similar to the pisland proce-
dure. It sets two Boolean variables, namely,
cooked and done to true. This effectively says that
the adventurer has been cooked and that the ad-
venture is over. Not much more to say, is there?
The pstart and ppit Procedures
Both of these procedures have slightly dif-
ferent case statements than the other location pro-
cedures in Adventure 1. In each, there is one direc-
tion that causes a special message to be printed.
This happens rather than either the location being
changed or the noway message being printed. In
each case, the difference is not important to the way
the game is played or to the result. The only pur-
pose for the extra messages is to add interest.
In ppit, ifthe player tries to go u, the message:
Try to climb that, and you'll kill yourself.
appears. This message is consistent with the ear-
lier description in location brink. There, the player
is warned that sliding down into the pit is possible,
but climbing back out doesn’t seem possible. The
ppit special message confirms this message.
The pvestibule, pnarrow1,
pladder, and pstart Procedures
In each of these procedures, one or more
Boolean variables is examined in order to detect
conditions that require special action. In each case,
further setting of Boolean variables may take place
depending on what happens.
In pladder, the variable carrying is ex-
amined. If carrying is true, it indicates that the
adventurer has located the treasure and is carrying
it. In this case, it is impossible for him to climb the
ladder, because the treasure is too heavy. Other-
wise, going up the ladder is allowed.
In pnarrow1, the variable carrying is ex-
amined again. If it is true here, the guide tells the
adventurer that going south with the treasure is not
feasible, because the crack in that direction is too
narrow. The adventurer is then given a chance to
drop the treasure. If the answer is yes, Carrying is
set to false and the variable dropped is set to true.
The latter variable is used in pvestibule later on.
In pvestibule, if dropped is true, the treasure
is sitting abandoned in location narrow1. The guide
tells the adventurer this and allows the treasure to
be reached for through the crack. If the adventurer
agrees to try to reach for it (can you imagine anyone
who would not agree to do that?), carrying is set
back to true and dropped is set back to false. This
allows the adventurer to win the game by then
returning to location start.
In pstart, the variable carrying is looked at. If
it is true, the variable done is set to true, which
means that the game is over. The fact that carrying
is true when the game ends means that the adven-
turer has succeeded in finding the treasure and
getting it back to the starting location. In short, the
adventurer has won.
The pogreroom Procedure
This location contains the most elaborate spe-
cial case code of any location. In fact, there is a
subsidiary procedure, ogreaction, which is in-
voked to handle a lot of the action.
The procedures pogreroom and ogreaction
deal with the adventurer’s interactions with the
ogre. There are only bad experiences to be had in
the ogreroom! The Boolean variables awake, and
eaten control these. If awake is true, you risk
being eaten by the ogre. Your chances are 1 to 5 if
you are not carrying the treasure, but 1 in 2 if you
are carrying it! If the ogre is not yet awake, each
time you visit his lair, you stand a 1 in 7 chance of
waking him.
If you should be silly enough to ask to go in
direction d while you are in the ogreroom, you will
also be eaten. This corresponds to walking directly
into the ogre’s fire-pit.
67
Preview of Adventure 2
The second example of a Pascal adventure game is
based on the first. The map has been extended
considerably, and many additional capabilities have
been added. This is the first “real” adventure game,
because it allows the player to issue commands and
control the play much more directly than the first
game did. In many ways, Adventure 2 is the heart of
the book. I shall continue to discuss it in one way or
another up until Chapter 25.
MAPS, DIAGRAMS, AND CODE OUTLINES
Figures 11-1 through 11-9 present the code
outlines, structure diagram, and maps of Adventure
2. Because Adventure 2 is an extension and modifi-
cation of Adventure 1, you should compare Figs.
11-1 through 11-9 to Figs. 5-1 through 5-8. The map
of Adventure 2 is similar to that of Adventure 1,
with some extensions. Figure 11-7 shows the dif-
ferences between the map of Adventure 1, which
was presented in Figs. 7-1 and 7-2, and the map of
Adventure 2. Figure 11-8 presents the map of the
additions to Adventure 1.
68
CHAPTER PREVIEWS
Chapter 13 is called “Command Processing in
Adventure 2” and discusses the use of enumerated
types in representing commands. It shows how the
cmdlookup function uses a linear search with a
sentinel to determine which command the player
has entered. In the process, it deals with arrays
indexed by an enumerated type, the input of values
of an enumerated type, the Pascal succ function,
and other related topics.
Chapter 14 is called “Carry and Drop: Pascal
Sets.” It deals with the use of the Pascal set types to
represent collections of objects in the adventure
game. In particular, it shows how the player’s
“stash,” that is, the objects being carried at a given
time, is represented by a Pascal set. It also shows
how the collection of items at each location is rep-
resented in a similar fashion. A comparison of the
use of Pascal sets with the implementation of sets in
ad hoc BASIC code presented. Finally, the use of
the set operators in Pascal is discussed in the
course of presenting the implementation of the
PROGRAM miniadventure;
CONST, TYPE, and VAR declarations:
This section of the program contains
the declarations of the constants,
types, and variables used by the
rest of the program.
See Figure 11-2
procedures and functions
This section contains the headers and
code for the procedures and functions
accessible to the main
program block and the outermost
level of the program.
See Figures 11-3 and 11-4
The main program block of Adventure 2.
See Figure 11-5
carry and drop commands in the adventure game.
Chapter 16 is entitled “Problems in Adventure
2.” Problems, as I have emphasized, are the soul of
adventure games. The techniques for representing
problems and their solutions in Pascal code are
therefore paramount. This chapter deals with the
use of Boolean variables and Boolean expressions
in the problem-solving process. It discusses the
particular problems posed by Adventure 2 and both
their external face, the way the player views them,
Fig. 11-1. The code outline for Adventure 2: the
program outline.
and their internal representation, the way they are
implemented.
The Pascal set membership operator, IN, and
its role in Boolean expressions is discussed. Other
elements of Boolean expressions are explained in-
cluding numeric relationships, and the AND, OR,
and NOT operators.
Chapter 16 deals with the ad hoc techniques of
Adventure 2. It is called “Other Techniques Used in
Adventure 2.” The Pascal coding solutions for
69
rooms
directions
cmds
} As in Adventure 1
represents the commands the user may issue in
Adventure 2 apart from directions
objects
represents the objects found in Adventure 2
collection
represents any subset of objects
placerec
a record type describing entries in the index
part of the descriptions database
xfile
narrate
file variables corresponding to the descriptions
database index and text data files, respectively
places: ARRAY [rooms] of placerec;
an array to hold a copy of the descriptions
database index in memory
whatshere: ARRAY [rooms] of collection;
an array corresponding to the set of objects
present at each location—modified by the carry
and drop commands.
stash
represents the objects carried by the player
cmdnames, objnames
Fig. 11-2. The code outline for Adventure 2:
important data declarations.
arrays of strings used to match command names
and object names typed by the player
hasdug
counts the number of times the player has dug
done, quit, carrying, dropped, chgloc, etc.
variables to track the occurrence of various
events during game play
counting the number of turns the player has taken,
displaying the contents of a set, scoring the game,
dealing with the lamp, and implementing the eat and
dig commands are explained. The handling of the
ogre character in the game is discussed. The
70
simplification of the process of changing locations is
explained, in particular, the function of the new
procedure travel.
Adventure 2 uses files for some of its informa-
tion. This is hinted at in Chapter 16. However, the
information in files is a complete data base of de- detail in the next section of the book starting with
scriptions of all the locations in the adventure Chapter 18.
game. How this database is created and all the Chapter 17 is a collection of helpful hints re-
techniques associated with it are dealt within great garding the use of the UCSD Pascal system. It is
PROCEDURE introduction;
PROCEDURE initialize;
} As in Adventure 1
PROCEDURE showobjects;
Tells the player what objects are present, if any, at the location
currently being visited.
PROCEDURE show;
Prints the full description of the location just entered.
FUNCTION score: INTEGER; } As in Adventure 1
FUNCTION objlookup: objects;
Determines which object name, if any, a user-typed string matches;
returns the internal value of said object.
FUNCTION ckobject (it: objects) : BOOLEAN;
Determines if a given object is present in the current location;
returns true if it is; false, otherwise.
PROCEDURE pcarry;
PROCEDURE pdrop;
PROCEDURE phelp;
PROCEDURE plight; Procedures used to
PROCEDURE pinventory support the execution
PROCEDURE ppush; of specific commands
PROCEDURE pdig; in Adventure 2
PROCEDURE popen;
PROCEDURE plook;
PROCEDURE peat;
FUNCTION cmdlookup:cmds;
Similar to objlookup, but determines commands rather than objects.
PROCEDURE listen;
FUNCTION docommand:CHAR;
FUNCTION whichway: directions;
PROCEDURE noway;
PROCEDURE travel (nloc,sloc,eloc,wloc,uloc,dloc:rooins);
Procedures and functions for general command handling and
changing player's location from one “room” to another.
PROCEDURE cklamp;
Monitors the lamp; warns player about staying in the dark, and so on.
Fig. 11-3. Code outline for Adventure 2: the procedures and functions: Part |.
71
PROCEDURE oreaction;
PROCEDURE pstart;
PROCEDURE pvestibule;
PROCEDURE pnarrow1;
PROCEDURE pisland;
PROCEDURE pogreroom;
PROCEDURE ppit;
PROCEDURE pladder;
PROCEDURE pflames;
Location procedures
as in Adventure 1. Because
travel is itself a
procedure in Adventure 2,
many locations no longer
need a location procedure.
(See Figure 11-5)
PROCEDURE pmaze; Location procedure for the maze.
PROCEDURE describe; Local procedures as in
PROCEDURE sameplace; Adventure 1.
PROCEDURE travel (nloc,sloc,eloc,wloc,uloc,dloc:rooms);
Local proceure—handles travel within the maze.
PROCEDURE pm1;
Local procedure—handles travel within the maze.
BEGIN
REPEAT
CASE location OF
maze,m1: pm1;
m2: travel (m1,same,m0,m0,m0,m0);
m19: travel (m0,m18,m0,m0,m15,m0);
END;
UNTIL (location<maze) or (location-flames);
END(+ PROCEDURE pmaze *);
Function block for pmaze handles travel inside the maze.
AREPEAT statement invokes the local travel
procedure until the player gets out of the maze.
Fig. 11-4. The code outline for Adventure 2: the procedures and functions: Part Il.
entitled “UCSD Pascal Development i1echniques.” your files, entering them into the system using the
There are discussions of how to manage your files UCSD Editor, splitting a large program into multi-
effectively and UCSD tricks and pitfalls. Organizing ple files, and using the include option in the com-
72
BEGIN
introduction;
initialize;
} As in Adventure 1
REPEAT
visited [location] := TRUE;
show (location);
cklamp;
Perform general processing associated with changing locations.
CASE location OF
start: pstart;
grandroom: travel (nowhere, nowhere, nowhere,
brink, nowhere, stairs);
vestibule: pvestibule;
narrow1: pnarrow1;
lakeshore: travel (island, narrow1, narrow2,
nowhere, nowhere, nowhere);
flames:pflames;
END;
UNTIL quit OR done;
Main control loop of Adventure 2. If a visit is made to a
location for which no special handling is required,
the travel procedure is invoked to process commands
and move to a new location. Other locations, for
example maze and narrow! still require location procedures.
These location procedures handle special conditions
as well as invoke travel.
congratulations; } As in Adventure 1
Fig. 11-5. The code outline for Adventure 2: the main program block.
piler are all touched on in the discussion. Acouple the UCSD system. Finally, a general discussion of
of filer tricks involving the prefix command and the _ pitfalls in software development under the UCSD
K(runch command are introduced—these trickscan system is presented.
save you time and trouble in your day to day use of
73
congratulations
introduction
cmdlookup
objlookup ckobject
Fig. 11-6. Structure diagram for Adventure 2.
74
lakeshore
deadend
Sara’
Fig. 11-7. Map of Adventure 2: Page 1.
75
2
=
fe}
°
>
®
c
fe}
£
Fig. 11-8. Map of Adventure 2: Page 2.
76
Fig. 11-9. Problems of Adventure 2.
You must carry the lamp and light it in order not to fall into a pit.
You must bring the treasure to start in order to win the game.
The treasure in Adventure 2 is locked in achest. You must find and
carrv the key—otherwise, youcannot open the chest when you
dig it up.
You must visit the island and read the message before you can
dig up the treasure.
The treasure in Adventure 2 is buried. In order to be able to dig for it,
you must locate and carry the shovel. When you dig for the
treasure, you must dig three times to fully reveal the chest.
You must drop the treasure at narrow1 and push it through the
crack leading to the vestible. There is no other way to get it out.
You cannot carry the treasure up the ladder—it is too heavy.
You must discover the trick of Problem 5.
77
Adventure 2’s description database file
can be found in Appendix B, page 286.
PROGRAM
CONST
f e
TYPE
roams
Pascal Adventure 2
miniadventures
(start, grandroam,
lakeshore,
ogreroam,
hatscave,
Maze, stai
incline, honeycomb,
mudroam, deeppool,
rockyroam,
vestibule, narrowl,
island, brink,
narrow2, pit, crystal,
Steams
rs, echoes,
narroaow4, river,
alcove, #1
AMES «
Mo, Mo, m7, MB,
mis, milo, mi?,
same, moawhere®)
mis, mi4,
directions = (Ny
78
Sy @,
deadend,
mo, mid,
Wey Uy Gd):
iceraam,
ladder,
WAIT TH CCIM 9
reundroam,
coldroom, narrows,
siltroam,
mo, ml, me, m3, m4.
mii, mis,
19, m19,
cmds = (carry, drop, help, light,
invent, take, tally, push,
dig, look, apen, unlock,
eat, moacmd) s
objects = (lamp, treasure, keys
sandwich, bottle, shovel,
nook j)s
collectian = GET OF abjects;
pname = §STRINGL40];
storyline = STRINGCS8OI;
byte = O.,.2553
whichsect = (indexsectian, descsection);
“whichsect” is changed to “whichsection” here; this doesn’t matter
to Apple Pascal, but may cause problems with other versions of Pascal
placerec
RECORD
CASE section Jwhichsection | OF
indexsections ( tableentry: INTEGER) ¢
deecsectian: { names pnames
id: INTEGER;
dbegins INTEGERS
dends INTEGER s
links byte 3
END s
VAR
xfiles FILE OF placerecs;
narrates FILE OF storylines
places: ARRAYCraoms] OF placerecs
whatshere: ARRAYC rooms] OF collections
visited: ARRAY Draams] QF BOOLEAN;
stash: collectian;
cammands: STRING;
79
80
{ holds user typed directian
head,
tails
{ hald SEFARATE words OF command
cmdnamess
ob jJmamess
che
dcharss
{ characters which correspond TO the
acceptable initial
directiean
C*n*,
lacatioan:
ogreloc:
speciall:s
nexts
turns:
is
indarks
hasdug
dane:
quit:
lits
eaten:
awake:
readmsqs
carrying:
STRING;
ARRAY Cemds] OF STRING;
%
4
%,
a
ARRAY Cob jects] OF STRING;
CHAR,
SET OF CHAR;
commands.
"ery Tw, TU,
rooms s
rooms:
SET OF roams;
directions;
INTEGER;
INTEGER;
INTEGER;
INTEGER:
BOOLEAN;
ROOLEAN;
BOOLEAN;
BOOLEAN;
BOOLEAN:
BOOLEAN;
BOOLEAN:
dropped: BOOLEAN:
trapped: BOOLEAN s
coakeds EQOLEAN s
candig: BOOLEAN §
chagloc: ROOLEAN:
CORO OK OK KF
t p @e }
OOK KOKO KK KD:
PROCEDURE wi pes
BEGIN
letters OF
initialized
7q°J
TO
“a
write (chr (ff) s
END §
{(SiaZ.ud,text?
($iaze,~us. text}
($ial.me2. text?
{fiad,.maze. text}
(Fia2.main,. text}
{ source FILE: aZ.ul.text 3 new file named “a2.u1.text”
CORO OK OK ROKK CKO KOK KK ROK KKK 4K XK 3:
{ i nt roo dou i « ¢t i Oo on +
OOK OOK OKO OKO OK KKK KKK KOK OK KKK K 3:
PROCEDURE introductians
REGIN
£
Wipes t clear screen
wreitelnmy
wreiteln ("Welcome to miniadventure!")3;
wreiteln ("Your goal will be to find a treasure’);
writeln (Cand bring it back ta your starting");
writeln (*point. You will also get points’);
writeln ("for finding each lacatian in the*);
writeln (adventure. Foints will be deducted’);
wreiteln (far various undesirable happenings: *);
writeln (waking the agre, getting eaten,*);
writeln (“getting toasted, etc.*);
wreiteln
writeln (*I will guide you and be your eyes")+s
writlen (and ears. Command me with one oar’);
writeln ("two word phrases, such as");
writeln (" "CARRY KEY" or "“NORTH". 7) 3
writeln ("I anly look at the first letter’);
writeln (’of commands that tell me which");
wreiteln (direction to take. Thus, i take’):
writeiln (? "NORTH" and "N" toa be the same.*) 3
writelns
writeln (* When you are ready ta begin your’);
weiteln (adventure, just press RETURN");
”
readin (command)
wipe;
rd
=2
END ¢€ PROCEDURE introduction
81
JOO OKOROKROK ROKK KKK OK KOK KOK KOK KK KKK KF
¢ i rn i t i a 1 i z =] 3
OOO OOOO ORK GOK KOK KKK KOK KKK}
PROCEDURE initialize;
VAI
locs raoams;
BEGIN
location := start;
dchars Pe is es a — er he ro
dane := false;
quit := false;
cooked r= false;
eaten r= false;
lit := false;
awake := falses
readmsq = false;
carrying := false;
trapped := false;
dropped := false;
candig := false;
chgloc r= trues
turns = OF
indark f= OF
hasduq r= O%
FOR loc := start TO nowhere DO
RBREGIN
visitedClac] := fa
whatsherelloc] :=
stash s= CI;
Csandwichdis;
Chottleds
whatsherelCdeadend]
whatsherelmudroaamd
whatsherelstart] = Clampd;
whatsherelCcoldraom] = Eshovel 1;
whatsherelCnarraw4] = Chey;
cmdnamesCcarry] :
cemdnamesCdropd r= "drop?
cmdnameslhelp 4 H
cmdnameslClight] g
82
cmdnameslinvent] = “inventory’s;
cmdnamesCtaked *take"s
cmdnames tally = "scare’ 3
cmdnamestCpush J = "push? 4
cmdnamesCdiqi = "dig";
codnamesllaak J = *jJoaak’*
a
= "apen” sy
= "unlock 3
= “@at" y
= "sentinel";
cmdnamesCopend
cmdnameslCunlockd
cmdnamesCeat J
cmdnamesCnacmdd
aeons ms oes ts 23 8s me ose BS
ob jnamesllampd = “lamp* s
eb jnameslCtreasurel:= “treasure’;y
objnamest€ shovel] := *shovel*;
ob jnameslkey r= “khey"s
ob jnamest(sandwichi:= “sandwich?’ 5
abjnamesCbottle] := “hoattle’;
ob jnameslnoobj)] := “sentinel’ ;
reset (xfile, *“a2.db80.x*)
“a2.db80.x” and “a2.db80” are the database files used
reset (narrate, *a2,db8o*)
by Adventure 2 for descriptions.
loc := start; Their location on disk is hard-coded into the game, and
seek (xfile, 31); Pascal will only look there. As written, Pascal expects
get (xfile); these files to be on the disk in Drive 1. See “A note
placesfClocd] := xfile ; about disk Drive numbers in file names and commands”
on page 3 of this PDF for more information.
REPEAT
loc := suce (lac);
get (xfiled;
placeslCloc] := xfile™;
UNTIL, loc = flames;
close (xfile);
END ¢ PROCEDURE initialize 3};
(OOO OOO OIOOIICIOIO CIO GOK KOK 3
{ 5s h oF w Oo 6b ji ec t $8 3
COORG OK K KKK 3
PROCEDURE showobjectss;
VAR
lobj: objectss
83
BEGIN
FOR labj := lamp TO noabj ba
IF lobj IN whatsherellocationd
THEN
REGIN
write ("There is a *)3
write (objnamesClabjd);
writeln (* here." )5
END ¢{¢ IF lobj IN .. 3
{ aenddo 3;
END ¢ FROCEDURE showoabjects 233
COOK OOOO KRACK KOK KK KKK 5
£ c=-9 h O w }
CeCe eeeeese se cesses sce Ses seeles 222255 2 2 2 3:
FROCEDURE show(where: rooms) &
VAR
i: INTEGER:
BEGIN
IF (chgloc AND lit)
OR
(location = start)
THEN
BEGIN
WITH places{where] DO
REGIN
FOR i s= dbegin TO dend bO
BEGIN
seek (narrate,1);
get (narrate) ;
write (narrate™) |
END ¢ DO 353
END <¢ WITH places 3;
END ¢ IF chglac +f;
84
showob jects;
END ¢€ PROCEDURE show 3
CKKK KKK KKK KKK KKK KKK KKK KKK KKK KKK KKK KKK
{ S Cc Q r e }
CK RK KKK KKK KKK KKK KKK KKK KKK KKK KKK KKK KK KG
FUNCTION score: INTEGER;
VAR
locs roams;
SCs INTEGER s
BEGIN
Sa s= OF
FOR loc :* start TO flames bdQO
IF visitedClaci]
THEN
Sc. 8 sa + &
{ endif +}
{ andda 4
TF NOT quit
THEN
sc = sc + 1405
IF caoked
THEN
SC #= sc = nil) §
IF eaten
THEN
Sc 8= sc - nO g
IF awake
THEN
ae
cor= sc ~ Bis
SCOre r= scx
END {¢ FUNCTION score ?}3
COCO OOOO OOK OK OKI OK OK 3
{ congratudlatians 3
OOOO OOO OOK HOOK OK IOKOK OK 7}:
85
FROCEDURE cangratulatians;
BEGIN
IF NOT cooked
THEN
BEGIN
IF NOT quit
THEN
BEGIN
writeln (° KKKKK Congratulations *xXKKK");
writelns
write (*You got the treasure aut in anly *)3
writeln (turns:4,* turns." )¢§
END;
writeln (* You scored *, score:4)3;
writeln (° points out of a maximum of FOO pts.*)3
writeln ¢(*So0 long for now, came again saon!*)¢;
END
ELSE
writeln (Sorry about that ~ try again soan!*)
{ endif 33
readin (cammand) ¢
Wipes
END ¢{ FROCEDURE cangratulatioans 33
COOK OKOOKRKOKKOK OK KOKOK KOK KKK}
{ ao b ji dl o oo k up 3
CROOK GOO COKOK KK XK KKK}
FUNCTION oabjlookup: objects
VAF
lobj: objects:
REGIN
abjnameslnoeobj] := tail;
labj := lamp;
86
WHILE tail <2 abjnamesllobj] pa
REGIN
{ Old debug statements ~ leave in
for historical edification.
write (tail);
write (oe):
writeln (objnamesllobj]);
+
lobj := succ(lobj)s;
ab jloakup := lobjs
END;
COCO OOOO OK OOK OK OK 2:
5
t c k oO Fb j @ c¢ t 3
OC OOKIORO OOOO OK Kok?
FUNCTION ckobject (it:objects): BOOLEAN;
BEGIN
ckobject := false;
IF it IN whatsherellocation])
THEN
ckobject := true
tC endif 34
END ¢ FUNCTION ckhobject 33
{ source FILE: a@w.us.text 3 neat Gleam So tent
COO OKO ORK KOCK KOK OK KOK KK 3
{ p C a r er y }
COCO OK OK KOK ROOK ROK OK CK KOK KOK >:
FROCEDURE pearry;
VAR
its ab jectss
BEGIN
it s= abjlockups;
IF NOT ckobject (it)
87
THEN
BEGIN
write (*'I don*’*t see any
write (tail);
writeln (¢(* here.’ )¢s
END
ELSE
BEGIN
writeln (7 Qk"*)s;
stash := stash + Cit;
whatsherelClocation] :=
whatshereClocationgy
END ¢€ IF NOT it IN «wan 35
END ¢{ PROCEDURE pearry 33
")s
Citd
COO OO OK OKKK KK OK KKK XK}
{
d r Q
FROCEDURE pdrop;
VAR
its ob jects;
BEGIN
88
it = objlookups;
IF NOT (it IN stash)
THEN
BEGIN
write (*You are not carrying any
wreitelm (tail)ds
END
ELSE
BEGIN
writeln ("Ok") 3s
stash := stash ~ Citds
whatsherelClacatian] :=
whatsherelLlocations
So
Pp )
OOOO OOOO OOOO OOIOICIOIGIGOO GIGI?
citds
as
2
*)
END {¢ IF NOT it IN stash 3;
END ¢ PROCEDURE drop 33
COCO OOO GOK OKO OK KOK KK D
{ p h e 1 p }
StSCCCCCeCesess Se Ce ses Less ss FF 2 2 5355 8;
FROCEDURE phelp;
BEGIN
IF readmsg
THEN
BEGIN
writeln (*The treasure is deep in the maze’);
END
ELSE
BEGIN
writeln (* There are hints to be found’);
writeln (*near the lake and in the alcove.’);
END { IF readmsg 33
END ¢ FROCEDURE phelp ?}3;
COO OOOO OOOO GOK TOK OK KKK >
{ p 1 i g h t 3
OOOO IIOIOR OIG OOO OK KOK AOK >
PROCEDURE plight;
VAR
cmd: STRING;
BEGIN
IF NOT (lamp IN stash)
THEN
writeln (You are not carrying the lamp.*)
ELSE
BEGIN
IF (tail *on*)
OR
(tail = “lamp’*)
89
THEN
lit s= true
ELSE
lit := false
{ endif ts
END ¢ IF NOT lamp IN stash 33
IF lit
THEN
writeln ("Your lamp is now on.”*)
ELSE
writeln (* Your lamp is of f.7)¢s
$
Ps
{ endif 33
END { FROCEDURE plight 33
colle
CK RK KKK KK KKK KKK KKK KKK KKK KK KKK KKK KKK KS
¢ Poionv @ nm ta riey +
ORR RR KKK ORK KKK KKK KK KKK KKK KKK KK KG
FROCEDURE pinventorys
VAR
lobj: objects;
BEGIN
IF stash <2 C7
THEN
BEGIN
writeln (* You are currently holdings
FOR lobj := lamp TO noobj DO
BEGIN
IF lobj IN stash
THEN
writeln (abjnamesllobj])
{ endif }
END ¢ FOR lobj :=
END € IF stash ¢3 CJ
END € FROCEDURE pinventory 33
{
CK KKK KKK KKK KK KKK KKK KKK KKK KKK KK KKK KK KD
90
COO OOKKAOK KK KKK KK KK KKK KKK KS
ut $ h 3
-
PROCEDURE ppushs
VAR
newtails STRING;
ps INTEGERS:
REGIN
p := pos (* *, taild;
IF p = ©
THEN
newtail :=
ELSE
BEGIN
newtail := copy(tail,ptl,length(tail)d-p)j
tail := copy(tail,1,p~1);
END ¢ IF pO 35
IF (tail = “treasure’*)
THEN
BEGIN
IF (treasure IN whatsherelCnarrowl))
AND
(location = narroawl)
THEN
BEGIN
writeln (7 Ok");
whatsherelCvestibule] :=
whatsherel[vestibule] + Ctreasurel;
whatsherelCnarrow1 ] :=
whatshereCnarrowl] ~ Ctreasureds
END ¢ IF treasure ws. 35
END
ELSE
BEGIN
writeln (*Sorry, but I don**t think’);
writeln (*?I can do that.")3;
END { IF tail = *treasure® 33
END ¢ PROCEDURE ppush 335
91
OOO KOK OK RR KOK KKK KKK KK KK KKK KKK KKK KG
t p d i g 3
CORO ROKK KKK KK KKK KKK KKK KKK KKK}
PROCEDURE pdig;
BEGIN
candiq := (shovel IN stash)
AND
Creadmsaq) s
IF (lacatian = mi)
AND
candig
THEN
hasdug := hasdug + i
{ endif 33
IF (leacation <3 m19)
THEN
writeln ("You can**t dig here at all.’)
ELSE
IF hasdug = 0
THEN
BEGIN
writeln ("You can’*t dig here yet.")s;
END
ELSE
IF hasdug = 1
THEN
BEGIN
writeln ("*That**’s a nice pile af dirt you");
writeln (*have shovelled. Re careful");
writeln ("not te block your way out! *")3
END
ELSE
IF hasdug = 2
THEN
REGIN
92
writeln (7?I see the top of a weather--*);
writeln ("beaten chest.” )s
END
ELSE
IF hasdug = 3
THEN
BEGIN
writeln (* You have unearthed an old");
writeln ("treasure chest. Tt is’);
writeln (*secured with a massive");
writeln ("brass lock.")s
END
ELSE
IF hasdug = 4
THEN
REGIN
writeln (* There’ *s nothing else here.’
writeln (but if you try hard, you");
writeln ("might diq dawn ta the*);
wreiteln Clava pits!")43
END
ELSE
IF hasdug = 4
THEN
BEGIN
cooked s= trues
dane f= trues
show (flames) ;
lacation s= flames;
chgloc f= trues
END ¢ IF hasdug = 4 3
{ END IF hasdug = 3 3
{ END IF hasdug = 3 3}
{ END IF hasdug =
{ END IF hasdug = 1
> 2
ar)
4,
2
)
93
{ END IF hasdug = O 3
{ END IF (location #2 m1i9 3;
ny
END {< FROCEDURE pdig 3};
COOK IOK GOK KK KKK ROKK KK KKK AK KKK KD
{ Pp oO p ein 3
COROROK KOK K KKK KKK AK KKK KKK KKK KR KKK KKK KD
PROCEDURE popens
BEGIN
IF (location = m19)
AND
(hasdug += 3)
AND
(key IN stash)
THEN
BEGIN
writeln (*Ok*)s
whatshere(€m19] := whatshere(€m19] + Ctreasured;
writeln (*The chest is full of treasure’);
END ¢{ IF 3;
IF (lacatian = mi?)
AND
(hasduqg == 3)
AND
( NOT (key IN stash))
THEN
BEGIN
Cl*'m afraid you’"11 need a key*)s
writeln
(*ta open that brass lack!")43
wreiteln
END {€ IF 33
IF location «<2: mil?
THEN
wreitelnm ( There’ *s nothing here toa apen!*)
{ endif 3;
END { FROCEDURE popen 34
94
COICO KOK KK RK OK OK KOK KKK}
¢ Pp tla ai ik }
CORR ROK ROKR KKK KOK KK XOKK KKK KK KKK KKK}
FROCEDURE plaoks
VAR
savcehaqs BOOLEAN;
BEGIN
gsavchg : chqgloacs
chqaloc := trues;
IF location «<= flames
THEN
show (locatian)
ELSE
END ¢ FROCEDURE plook 3};
CORO KKK RK KKK KR KKK KK KKK KKK KD
{ p e a t 3
CROOK KK KK KKK KK KKK KKK KK KKK KK KD
FROCEDURE peat:
BEGIN
IF sandwich IN stash
THEN
BEGIN
writelnm (Oh, yummy! !*") 3
Stash := stash ~ ECsandwichds
END
ELSE
IF tail = “sandwich’
THEN
writeln (* Yau don*"t have a sandwich’)
ELSE
writeln (* Don’ *t be ridiculous! !*)
+
{ endif 3
95
{ endif 3
END ¢ PROCEDURE peat 33
CORO OKO KK HO KKK OK 3:
¢ c m gd lof ag k ww p 3
COCO ROOK KK KOK KOKORO KKK KKK KK KG
FUNCTION cmdloakup: cmds;
VAR
p: INTEGER;
lemd : cmdss
BEGIN
wreitelms
write (7 === 2");
readin (command) ;
p := pos (* *, command);
IF p=0
THEN
BEGIN
= copy (command, 1, pl);
tail :=
copy (command, pti, length (caommand)~p)s
END ¢ IF p=0 3};
cmdnamesCnocmd] := head;
lemd := carry:
WHILE head <> cmdnameslClemd] DO
lemd s= suce (Clemd)
{ END DO }3
cmdlookup := lems;
END ¢€¢ FUNCTION cmdlookup 33
COOK COIR CCK ROKOKOK OK KKK KK KD
{ 1 i $s t ein +
COO O OOOO OK OK OK ROK KKK OK OK KK KD:
96
PROCEDURE listen;
VAR
lemd: cmdsy;
BEGIN
REFEAT
lemd := cmdlookups
CASE lemd OF
carrey: pearrys
drap:s pPdraps
helps phelps;
light: plight; add a space between
x ares pinventorys comma and apostrophe
takes pearrys
tally: BEGIN J
write ("*Ghould you quit mow," )3
writeln ("your score would be *)5
wreiteln ((score 140) :3)3
writeln (* points of a possible 300,’
END;
pushes ppushs;
dig: pdiqs;
loaks ploaks
opens popens;
unlocks popen;
eats peat;
nocmds $
END:
UNTIL lemd = nocmd;
END ¢ PROCEDURE listen 33
COOK OOOO OO OOK OOK OK OK x 3
{ do «c¢ o m m aon ad }
COO ROOOK OKRA KK KKK K KKK KKK KKK KKK KK KD
FUNCTION docommand: CHAR;
BEGIN
head s= "75
tail r= 7" 5
REFEAT
listen
UNTIL length (head) = Of;
docommand := headCilds
END ¢ FROCEDURE docommand 133;
COOK OK KKK OK OK KOK KK}
,
{ w Hh i c¢ Hh w aiey 3
COR ORK KOK KK KKK KKK KK KKK KKK}
FUNCTION whichway:directions;
BEGIN
turns = turns + 1;
REFEAT
ch := docommand;
CASE ch OF
*n's whichway := m3
*@?s whichway := s3
"e's whichway := eF
7wis whichway : = wy
7? is whichway s= us
"d’"s whichway s= ds
?q's quit s= trues
END;
UNTIL ch IN dchars;
writelns;
end ¢ function whichway 3;
CK KKK KK KKK KK RK KKK KKK KK KKK KKK KKK
{ n Oo w a y }
CORR KKK KKK RK KKK KKK KKK KKK KKK KKK KK)
98
add space betwen
procedure noways go and apostrophe
begin |
writelns;
write (*There is no way to go");
writeln (’in that direction.’)s
chglac := false;
END;
COO OO OK OK KKK OK OK K
t Cc kk 1 a m Pp }
COO OOOO OK OK KOK OK ORCK OK KOK 3
PROCEDURE cklamps
BEGIN
IF (location «<> start)
AND
NOT lit
THEN
indark 3:
{ endif 35
IF indark = 4
THEN
RBREGIN
quit s= trues
cooked = trues
dane r= trues
writeln ¢(* You fell into a pit and were’);
writeln ("*killed. Too bad!! Maybe next’):
writeln (time you"’11 listen to me*);
writeln (‘and keep your lamp on!");
END
ELSE
IF (indark 3} ©)
AND
(NOT lit)
AND
(lacation “+ start)
99
THEN
REGIN
writeln ("It is now pitch dark.") 3
writeln ¢(* I wouldn’**t go too far.*);
writeln (* You might fall into a pit AND
writeln ("killed.")s;
END ¢ IF indark + © 33
{ END IF indark + 4 3;
IF (turns += 75)
AND
(turns < 80)
THEN
BEGIN
be" )¢
writeln (7? I would think about wrapping this");
writeln ("up, since your lamp is getting dim.
END
ELSE
IF turns = 150
THEN
REGIN
wreiteln (You’?re in trauble mow! *)5
weiteln (Your lamp just went aut! *)¢3
lit = false;
END ¢{ IF turns = 150 3
{ END IF turns = 7S wae FY
END { FROCEDURE cklamp 3;
OOOO KKK OK OK ROOK KKK KK >
£ t r a Vv e il 3
COOOOROROKKOOK KK KOK K KK KK KKK KKK KK KK KD
PROCEDURE travel ¢
nloc,
sloc,
eloc,
wloc,
100
2
7)
uloc,
dloc: rooms) ;
PROCEDURE newloc(loc:roams)
REGIN
IF loc=nowhere
THEN
chgloc
END ¢ IF 33
END { FROCEDURE newloc }3
BEGIN {kx travel xx}
CASE whichway OF
ms newloc (nlac);
gs: newloc (sloac)s
@: newloc (eloc);
we newloc (wloac);
us newloc (ulac)s
ds: newloc (dloc);
END ¢ CASE whichway 33
END (¢ FROCEDURE travel 33
{ source FILE: a@.m2.text 3
COOK OKOK KKK KKK KK KOK KK KG
if oO gr @ a c t i oO nn 3
COKOOKOKKROK OK KKK KOK KK KK KKK KKK KKK KKK KG
new file named “a2.m2.text”
PROCEDURE ogreaction;
REGIN
IF NOT awake
THEN
BEGIN
writeln ("This is the ogre’*s lair!");
writeln (If you are not careful, you’? 11");
writeln (*wake him.*)3;
IF (turns MOD 3)=0
101
THEN
BEGIN
awake := trues
writeln ("Now you" "ve dane it!")s5
writeln (*You woke the ogre - better’)s;
weiteln ("get out of here while you can’);
writeln (* You wouldn’*t listen ta me would’);
writeln ("you? You really better get out’);
writeln (of here before you get eaten! *),
IF treasure IN stash
THEN
IF (turns MOD 2)=0
THEN
BEGIN
writeln (*Too bad!! The ogre caught you");
writeln ("and roasted you for dinner.’):
wreiteln ("Better luck next time!!")s3
eaten = trues
quit o:= trues
END
ELSE
BEGIN
writeln ("Get out fast if you don*."t want’);
writeln (to be a big-mac far the ogre! !")45
‘Big Mac’ if we’re getting technical...
END
ELSE
IF (turns MOD 2)=0
THEN
REGIN
writeln ("Too bad ~- you" "ve been eaten! ");
102
eaten 2 trues
quit f= trues
END
Q %.
{ endif 3}
END ¢ IF NOT awake 33
END ¢ FROCEDURE ogreaction 33
COCO OOOO KIO KOK KOK KOK K 7:
t p S t. a r t >
OOOO OKO KKK KOK OK 3:
FROCEDURE pstart;
REGIN
IF treasure IN stash
THEN
done : = true
ELSE
REGIN
CASE whichway OF
Ms Bp @aWe noways
us writeln ("You can**t jump to the clouds!*);
d: location := vestibule;
END {€ IF carrying f3
chgloc := (location «<> start);
END ¢ PROCEDURE pstart 343
COOK GOK OK OK KOK 3
if p v @ Ss t i bh u | @ »
ORO ROKK OK OK OKKOKOK ROK KOK KOK KK KK KD
PROCEDURE pvestibule;s
BEGIN
103
IF treasure IN whatsherel€narrowl] — addspace between
THEN narrow and apostrophe
BEGIN
write (’To the north, through a narrow’);
writeln ("crack,*)3
writeln ("you can see the treasure. ")3
END { IF treasure ... 33
travel (narrowl, grandroom,iceroom,
nowhere, start,nowhere) ;
END {¢€ FROCEDURE pvestibule 3;
COOK KOK ROKK KK KOK KK KK KKK
t Pp na rr aiew i il 3?
COCO OKO KOKORO OK KOK K 3}
PROCEDURE pnarrowl:
BEGIN
CASE whichway OF
= lakeshore;
ogreroom;:
*?s too narrow to get through! ")s;
ne location :
es location =:
Ss writeln (?*
It
wWyltads noways
END ¢ CASE whichway 73;
chgloc := (location “<> narrowl)s
END { PROCEDURE pnarrowl 33
COO OOK OOOO KOK KK}
{ p i 5s lt aooni qd 3
OOOO OOK OKO KK KK KK KKK
PROCEDURE pislands
REGIN
travel (nowhere, lakeshore, nowhere,
nowhere, nowhere, nowhere) 4
readmsqg := trues
104
END { FROCEDURE pisland ty3
OCR KOO ROOK OKO HOOK KOK 7
t Q g r e r Q Q m }
COOK KOO OK GK KKK OK KKK 3:
PROCEDURE pogreroaaoms
VAR
re INTEGER:
REG IN
agreactions
IF NOT eaten
THEN
BEGIN
wreiteln ("There are exits to the east,");
writeln ("*north, and west");
CASE whichway OF
Ws location = narrowl:
e: location := batscaves
ns location :* narrow2;
ds:
BEGIN
quit 2 trues
eaten 8 = trues
writeln (?Oh no!!! You dummy!!!" )s
writeln ("You just fell into the firepit’):
writeln (and made such a ruckus that");
writeln ("you woke the ogre. I hate to’);
writeln (’tell you this, but you are’);
writeln ("also trapped!’);
BEGIN
FOR j s= 1 TO 1000 DO;
write ("2.7") 3
END { DO 33
writelngs
writeln (*You"’* ve just been added to the’);
105
Cogre’’*’s gourmet recipe library!*)3
writeln
(Better luck next time.*)s
writeln
ENDs
Salk NOWwAYs
woe
v8
END {£ CASE whichway
END ¢ IF NOT eaten 33
chgqloc := (location “> agrerooam) ;
END { FROCEDURE pogreroaam 33
new file named “a2.maze.text”
%
{ source FILE: aZ.maze.text 3}
COKK OKRA KKK KKK KKK KKK K KKK KKK KKK KKK KK KG
{ p m a z e }
COOK OKK KK AK KKK KKK KK KK KKK KKK KKK KK KK KD
PROCEDURE pmazes
TYFE
Mazerooms = mO,.same;
VAR
mazelac: MAZeroamMss;
FROCEDURE describe;
BEGIN
writeln ("You are in a maze of *)s
writeln (’featureless passages." );
writelns;
end { procedure describe 3;
procedure sameplace;
begin
(7? You have crawled around’);
{*some twisted tunnels and*);
(*7wound up where you began.”*)3;
writeln
writeln
writeln
%
END { PROCEDURE sameplace 33;
FROCEDURE travel (¢
nloc,
sloc,
106
eloc,
wloc,
uloc,
dlocs rooms);
FROCEDURE newloc (loacrrooms) ;
BEGIN
IF loc=moO
THEN
noway
ELSE
IF loc=same
THEN
sameplace
ELSE
location := loc
{ endif 3
{ endif 33
END ¢ FROCEDURE newloc +3
BEGIN (xk travel XxX?
CASE whichway OF
ns newloc (nloc)s
Ss newloc (sloc)s:
e: newloc (eloc)s
we newloc (wlac)s
us newloc (uloc);
ds: newloc (dloc);
END { CASE whichway 33
END ¢ PROCEDURE travel 33;
PROCEDURE pmls
BEGIN
writeln ("From here you can go south,
writeln (west, or up.") 3
CASE whichway OF
Ss location := ladders;
e: location s= m2;
east.” )3
107
us location
nads naways
END;
END { pmi 33
ws lacation
BEGIN <¢ FROCEDURE pmaze 34
REFEAT
IF (location “> mi)
AND
(location <> maze)
THEN
108
describe
{ endif 3¢
showoh jects;
ckhlampy
CASE locatian OF
MAZE,
mis pml;
ms travel
mos travel
m4s travel
ms travel
més travel
m7: travel
m8: travel
mo s travel
mids travel
mids travel
mi2s travel
miss travel
mi4: travel
mics:
BEGIN
(m1, Same, mM, mO,MO,MO) 3
(m1, m0, same,mO,mO,mO) 5
(mO.M7 4 M34 MF, MO, MO) §
(m1,mO,MO,MO,MO,mMO) 3
(m4,m0, same,~mO,mO,mO) 3
(mS,m?,mo,mB,~mO,mO) 5
(m,MO,MO, SAMS,MO,MO) §
(mO,M11,MmO,M1O9O,MO,MO) §
(mB, Same,MmO,~mMO,mMO, MO) 5
(m?,MO,MG,M1O,mi,~mi2);:
(m12,mO,MmO,~MmO,MO,m14G) §
(m14,mO,mMO,mMO,MO,m17) |
(m1S,mO,mo,mO,mO, M18)
writeln (° I seem to remember buried’);
writeln ("treasure near this location.”*)
travel (mO,mO,mO,mO,m1,~mM1?)
END ;
midé: travel (m17,same,mO,mO,mO,mO) ;
mi7: travel (m18,.m16,mO,MmO,MO,mMO) 5
mis
BEGIN
writeln (’I seem to remember a treasure’);
writeln (*near here.*)3
travel (m19,m17,mO,mO,MO,mMO)
END;
mivs travel (mO,m18,mO,mOo,m1S,mO) :
END {¢ CASE location 3,
UNTIL (location «< maze) OR (location = flames):
chqloc := trues
END ¢ FROCEDURE pmaze 33
{ source FILE: a@.main.text } new file named “a2.main.text”
OOOO OOOO OO OOOO II IIE:
£ p p i tt }
OOOO OO OOOO OGIO IO OOK IK 3
FROCEDURE ppits
BEGIN
CASE whichway OF
d: location := ladders; multiple statements
us x writeln (*Try ta climb that’); in a case selector
writeln (Cand you’ "ll kill yourself!*)s; must be bookended
END; by BEGIN and END;
Ty Sa @qgws naways
The BEGIN statement must be added
END; between u: and writeln as
chgloc := (location <> pit); indicated by the asterisk.
Type “u: BEGIN” and press return.
END ¢€ FROCEDURE ppit 35, Tab over under BEGIN and continue
typing the writeln instructions,
CROOK OK CK KOK KKK KKK KKK followed by “END;” as shown.
{ Pp tladqddeor }
COC OOOO OOK ORK KKK KK}
FROCEDURE pladder;
BEGIN
109
CASE whichway OF
ms location t= maze;
d: location := flames;
us
IF treasure IN stash
THEN
BEGIN
writeln (You can’’*t carry the treasure");
writelnm ("up the ladder -")3
writeln ("it*"*s much toa heavy! ");
END
ELSE
location := vestibule
{ endif 33
@,.S_Ws NOwWAYy s
END;
chgloc := (lacation «<> ladder) 3:
+
END ¢ PROCEDURE pladder 33
CREST EESRE EDEL EET CEES EL LESS EERE SESE RS
{ p ag 1 a m e 3
STUTTTETTRTTTTUTTTTTETTTT TET TOTTORI TS
FROCEDURE pflames;
BEGIN
cooked := trues
done f= trues
END {€ FROCEDURE pflames 3};
COOK OOK OK KOK OK KKK OK OK}
BEGIN { =ee==> adventure {ss=== }
CKKKKKKKKKRK KKK KKK KKK KKKK KG
introduction;
initialize;
110
REPEAT
visitedClocatiand := true;
show(locatioan) 3
cklamp;
CASE location OF
start:
grandroams
vestibule:
narrowls:
lakeshore:
islands
brink:
iceraoms
ogreraams
narroaw2s
pit:
crystals:
batscave:
steams:
deadend:
ladder:
Mazes
stairs:
pstarty;
travel (nowhere,
nowhere,
nowhere,
pvestibules
pnarrawl;
travel (island,
narrow? ,
nowhere.
pislands
travel (nowhere,
nowhere,
nowhere,
travel (ogreroom,
crystal,
nowhere .
pogreraams
travel (nowhere,
nowhere,
brink,
stairs):
narrowl,
nowhere,
nowhere) 5
nowhere,
ogreroom,
pit);
nowhere.
vestibule,
nowhere)
ogreraom,
steam, lakeshore,
NOW Er & .
ppits
travel (ogreraom,
nawhere) 3
nowhere.
Maze, agreraam,
nowhere,
nowhere) 3
travel (steam, moawhere,
nowhere,
nowhere,
travel (deadend,
nowhere,
nowhere,
travel (nowhere,
nowhere.
nowhere,
pladder;
PMaAZes
travel (nowhere,
nowhere.
ogreroom.,
nowhere)
batscave,.
narrows.
MAZE) §
steam.
nowhere,
nowhere) §
nowher &,
nowhere.
111
END ¢
112
echoes:
inclines
warmrooms
roundrooms:
honeycomb:
mudroams
deeppool:
caldroom:
narrows:
narrow4s:
rivers
rockyrooms:
siltrooms
alcoves
flames:
CASE
grandraom,
travel (warmroom, incline,
nowhere, nowhere,
stairs, deeppool);
travel (nowhere, nawhere,
roundroam, honeycomb,
echoes, nawhere) 3
travel (nowhere, echoes,
nowhere, nawhere,
nowhere, flames);
travel (honeycomb, nowhere,
nowhere, incline,
nowhere, mudroam) §
travel (nowhere, narrows,
incline, nowhere,
nowhere, nowhere) s
travel (nowhere, nowhere,
nowhere, nowhere,
roundroam, siltroom)s:
travel (nowhere, nowhere,
nowhere, nowhere,
mudroom, coldroaam) ;
travel (nowhere, nowhere,
nowhere, nowhere,
deeppool, nowhere):
travel (honeycomb, narrow4,
nowhere, nowhere,
nowhere, nowhere) ;
travel (narrows, river,
nowhere, nowhere,
nowhere, maze) s
travel (narroaw4, rockyroom,
nowhere, nowhere,
nowhere, nowhere);
travel (river, siltroam,
alcave, nowhere,
nawhere, nowhere) 5
travel ¢rockyream, nowhere,
nowhere, alcove,
muidraam, nowhere) ;
travel (nowhere, nowhere,
siltroom, nowhere,
nowhere, nowhere) 3
pflames;
+
location 33
echoes) §
UNTIL quit OR done;
cangratulationss
END.
113
Command Processing in Adventure 2
This chapter is one of the most important of the
book. It deals with topics that, taken one at a time,
are pretty simple, but when put all together form a
major part of all our subsequent adventure game
implementations. You will probably want to read it
more than once. It is also a good idea to refer to the
actual Pascal code frequently while reading.
The first example of an adventure game would
be considered a mere “toy” by devotees. Why?
Mainly because it did not allow the player to use
traditional adventure game commands. It limited
the player to travel commands or directions. The
consequence of this was the fact that “solving” all
the problems was totally unchallenging. It was sim-
ply a matter of answering yes or no questions (with
most of the successful answers obviously being
yes). Of course, you want your own adventure
games to allow “real” command processing.
Commands are important because they add a
sense of control to the game. The player is par-
ticipating. Not everything is yes or no. Even the
commands that the guide will recognize and carry
out must be guessed by the player. Beyond that,
commands provide the vehicle by which players
solve the problems posed by the adventure game.
This is the essence of true adventure, and without it
adventures are not really worth playing.
In Adventure 2 I have included commands that
take the form of one or two word sentences. These
are simple verbs or verbs with a single noun as
object. Such commands provide adequate complex-
ity. More advanced adventure games allow a vari-
ety of multiple word commands. Such games often
claim that the player can use English commands.
This is an overstatement. Most English sentences
will completely befuddle such adventure games. In
any case, implementing a parser for such commands
is a much greater challenge than I am prepared to
attempt in this book. So I will stick to our one or two
word commands. Even these will allow you to im-
plement interesting problems in your adventures.
The commands that have been added to the
game are carry, drop, help, light, inventory, take
(synonym for carry), tally, push, dig, look, open,
unlock (synonym for open), and eat.
There is one support procedure for each possi-
115
ble command. These procedures are named by pre-
fixing the letter p to the command name itself. So
for example, there are the procedures pcarry,
pdrop, plight, and so on. The command support
procedures are to commands, what the location
procedures are to locations. They provide all the
Pascal code needed to carry out the corresponding
command. The commands that are synonyms for
other commands use the same support procedures
as the commands for which they are synonyms.
AN OVERVIEW OF THE COMMAND HANDLING CODE
Figure 13-1 shows the procedures and func-
tions used by Adventure 2 for command processing.
The top levels of this diagram duplicate Adventure
1. What is new are the functions and procedures
below travel and whichway—docommang, listen,
and cmdlookup, which are various procedures de-
signed to do special processing for individual com-
mands. Much of the code that I describe in this
chapter can be reused in all of your adventure
games.
The travel Procedure
The travel procedure causes either a new lo-
cation to be determined or the message “There is
no way to go in that direction” to be printed. The
travel procedure invokes the whichway function to
determine which direction the player desires to
take. I discuss travel in more detail in Chapter 16.
The whichway Function
The whichway function is also similar to the
one used in Adventure 1. It differs in that it does not
contain the code for prompting the user. Rather
it invokes a new function called docommand;
docommand returns a single character that indi-
cates the direction the player wants to go. There is
the possibility that this character does not corre-
spond to one of the legal travel indicators. In sucha
case, the command is simply ignored and the player
is reprompted for a command.
There are two weaknesses with the command
processing in Adventure 2 that will be remedied in
Adventure 3:
116
gw Any word beginning with a travel letter (n, s, e,
w, u, or d) is taken to be equivalent to the travel
command. Thus, if the player types NEVER-
MORE, the command processor will assume that
the command was n, or north.
w@ If a command that is neither a legitimate com-
mand nor a direction indicator is typed, the
player is simply reprompted. There is no indica-
tion given that the guide failed to understand.
__ This can be misleading at times—the player may
‘ assume that the command was understood. For
example, TURN ON LIGHT will be ignored, and
the lamp, if not yet lit, will stay unlit.
The docommand Procedure
The docommand procedure is really a “front”
for listen, which does the real work. the docom-
mand procedure sits in a while loop calling the
listen procedure. The listen procedure eventually
returns, having set the variables head and tail. The
docommand procedure returns the first character
of the head string. It makes sure this is valid by
first determining that there is something in the
string to return. This is accomplished by using the
intrinisic function length:
length (head) > 0
The listen Procedure
The listen procedure is the command dis-
patcher for Adventure 2. It supervises the process
of determining which command the player wants to
have executed. It invokes the appropriate command
procedure for each command entered.
The cmdlookup Function
The parts of a compiler that read, interpret,
and verifiy the syntax of a program are called scan-
ners and parsers. The cmdlookup function provides
a very rudimentary scanner and parser combination
for Adventure 2. It prompts for the players com-
mand and breaks that command into two pieces—
head and tail. The head string always determines
which command will be executed. The tail string
will contain the object of the command verb if there
arection AY
docommand
listen
cmdlookup
Ka
Fig. 13-1. The structure diagram of the command processing in Adventure 2.
is any. It is always possible for tail to be the empty As I hinted earlier, cmdlookup is not at all
string: length (tail) = 0. sophisticated. It doesn’t even bother to make sure
117
that the character strings in head and tail are sensi-
ble for command and object names. It will blithely
allow you to type:
*&ANO$H@W(2>< —--++==
as a command. When it fails to match the string
& %# '(/2>< to the name of any legal command
or direction, it will reprompt you for another com-
mand. It ismonumentally stupid! However, it is just
smart enough to do the required job.
COMMAND PROCESSING IN DETAIL
Parts of the command processing code involve
some rather complex but very useful programming
techniques. These programming strategies are
detailed in the following pages.
docommand: Calling listen in a Loop
There really isn’t much more to say about
docommand. It conveniently separates an enclos-
ing loop around listen. That loop could have been
incorporated into listen itself. The only difference
would be a miniscule gain in efficiency. Because
docommand is involved in direct response to in-
teractive commands, you probably wouldn’t even
notice the difference. If you are the curious type,
making that modification would be a good exercise
to try.
listen: Another Case Statement for Control
The listen procedure uses a local variable
Icmd of type cmds to control its case statement.
The cmds type is an enumerated type that names
all the commands available to the player:
cmds = (carry, drop, help, light, inventory,
take, tally, push, dig, look, open,
unlock, eat, nocmd);
I shall discuss the use of this type and the setting of
the variable Icmd in detail below.
The REPEAT . . . UNTIL loop in listen en-
sures that the player’s commands are carried out
118
until a command that is either a direction or an
unrecognizable sequence is entered. All of the fun
takes place inside the cmdlookup function, and I
devote the remainder of this chapter to its work-
ings.
The cmdlookup Function
The cmdlookup function is not very long—a
little over half a page of well-spaced Pascal source
code. Yet it packs a lot of wallop. In the course of
unraveling its inner mysteries, I shall delve into the
following subjects:
@ The input of enumerated types.
@ Arrays indexed by enumerated types.
@ The conversion of values—external to internal.
@ The method of searching an array using a linear
search.
@ Arrays of strings and string comparison.
@ Sentinels in a linear search.
@ The succ function and its use in searching.
There is a lot to cover. The only way to reach the
end is to plunge ahead. So, here we go!!
The Input of Enumerated Types. I have
done more than a little to give Pascal’s enumerated
types “rave reviews.” Now I come to one of the
annoying weaknesses of enumerated types in many
Pascal implementations: it is usually impossible to
READ or WRITE a value of an enumerated type
directly, that is, using the identifiers contained in
the declaration of the enumerated type.
In Adventure 2, this is exemplified by the array
of strings that I have called cmdnames. cmd-
names is indexed by the enumerated type cmds:
cmdnames: ARRAY{[cmds] of STRING;
In order to really understand what is going on
here, I need to elaborate on this declaration and its
implications for the Pascal language. Bear with me
while I digress from the consideration of the actual
code of cmdlookup.
Arrays Indexed by an Enumerated Type.
I am assuming that you know what an array is. I also
assume that you have used arrays before in your
Pascal programs and are familiar with the syntax for
declaring arrays. However, the idea of using an
enumerated type as the index for an aray may be
new to you.
Ordinarily, the index of an array is simply a
range of integers
x: ARRAY[1 . . 100] OF STRING;
and individual elements of an array are referenced
using single values from the range as sub-
scripts—x[1], x[5], x[29], and so on. An array cor-
responds to a list of items. The index is like the
number indicating the position of the item in the
list. Because the identifiers of an enumerated type
are assigned numeric values by the Pascal com-
piler, you may think of the enumerated type itself as
being just like a range of integers. Therefore, using
an enumerated type as the index of an array in a
declaration is a normal thing to do.
When an array is indexed by an enumerated
type, you can think of the array as a whole as a
“named list” of items. To illustrate, consider the
following list of men’s nicknames. The list is enu-
merated by the names for which nicknames are
being listed:
Name Nickname
Richard Dick
Thomas Tom
Lawrence Larry
William Bill
John Jack
Arnold Arnie
James Jim
This list may be represented in Pascal as an array of
strings indexed by the enumerated type:
name = (Richard, Thomas, Lawrence, Wil-
liam, John, Arnold, James);
The array declaration would look like this:
nicknames: ARRAY[name] OF STRING;
Then assignment statements such as
nickname[Richard] := ’Dick’;
nickname[Lawrence] := ’Larry’;
nickname[James] := ‘Jim’;
are perfectly legitimate. Each one constructs one of
the entries in the original list of nicknames given
above.
The Conversion of Values: External to
Internal. The identifiers in a Pascal enumerated
type represent external values. The numbers as-
signed to the identifiers in an enumerated type
represent internal values. They are the values that
the Pascal compiler prefers to use when ma-
nipulating the enumerated type inside the comput-
er.
In the best of all possible Pascal implementa-
tions, you should be able to write the following sort
of code:
writeln (“Your wish is my command > ”);
read (Icmd);
CASE Icmd OF
Carry: pcarry;
drop: pdrop;
help: phelp;
light: plight;
invent: pinventory;
and so on. That is, given a variable of type cmds,
such as Icmd, you should be able to read a value of
Icmd. The user of the program containing the above
code should be able to type one of the identifiers in
the enumerated type in response to the prompt, for
instance
Your wish is my command > carry keys
This should have the effect of the identifier carry
being read and the corresponding internal value of
the enumerated type being stored in the variable
Icmd. (The second part of the input line would be
ignored until another read command was issued by
the program—presumably this would take place in
the pcarry procedure.)
119
This is an example of the concept of conversion
of representations. It is worth talking about this a
little further to give you an idea of why our “best of
all implementations” scenario is usually not the
reality.
What kind of values are stored in a variable like
Icmd? In the guts of the actual Pascal program, they
are whole numbers: 1, 2, 3, and so on. The actual
numbers are not guaranteed to be any particular
values. The definition of Pascal only requires that
the numbers assigned be consecutive. It is true that
90% of all Pascal compilers will assign values be-
ginning with 0. I would venture to say 100%, but
then I am not personally acquainted with every
Pascal compiler ever written! The correspondence
between the identifiers in the declaration and the
numbers chosen to represent these identifiers is
maintained by the Pascal compiler. So whenever
your program refers to one of the identifiers in the
enumerated type, the right numeric value is sub-
stituted for it in the translated program.
Once a Pascal program has been compiled, the
identifiers of an enumerated type are forgotten.
They have all been assigned their internal numeric
values. This is fine as long as you never wish the
program to input or output a value of any enumer-
ated type. Should you wish to do so, you are faced
with the following fact: the identifiers (as they ap-
pear in the declaration of the enumerated type) are
character strings, but the internal values corre-
sponding to those identifiers are numbers. For
example, the cmds enumerated type might be as-
signed internal values as follows:
External (identifier)
carry
drop
help
light
inventory
take
tally
push
dig
look
open
Internal (number)
SCOONODOUFWNHKH OC
—
120
External (identifier) Internal (number)
unlock 11
eat 12
nocmd 13
The question becomes how do you “compute”
a variable whose type is a user-defined enumerated
type? In particular, how do you input such a value in
response to a user’s command that is typed as a
character string. The answer is basically pretty
boring. You input the external representation of
such a value and convert that value to its internal
form yourself. This requires writing explicit Pascal
code to do the job. In Adventure 2 the procedure
cmdlookup performs this task.
The first piece in solving the puzzle of enu-
merated type input is an array of strings. The array
must be indexed by the enumerated type. The
strings in the array are the identifiers used in the
declaration of the enumerated type. Thus for cmds,
you have the array cmdnames, mentioned above.
For example, cmdnames{inventory] := inven-
tory ; The array cmdnames is set up in the proce-
dure initialize. This is similar to the example of
nicknames that I gave earlier.
How do you use the string array? You use it to
match a string typed by the user to one of the
identifiers in the cmds type. This is the lookup
aspect of cmdlookup.
Actually, the strings read by cmdlookup may
consist of more than just the command name itself.
This is true because many command verbs take
objects: carry keys, drop treasure, light lamp, eat
crow, and so on. Also, the player may perversely
type anything at all that forms a legal string: “Take
me to your leader,” or “Come with me to the Cas-
bah.” cmdlookup must first dissect the input string
into two parts, which I have called head and tail. So
cmdil-okup first reads the player’s response to the
prompt into the string variable command. It dis-
sects Command into two pieces by locating the first
blank in the command string (if any). The part of the
command preceding the first blank is stored in the
variable head. The part of the command after the
first blank (but not including it) is stored in the
variable tail.
The variable head will be used in the com-
mand lookup process. The variable tail is used by
the various command support procedures to deter-
mine the details of specific commands. For exam-
ple, if head is the command carry, tail will be used
to determine what, if anything, will be carried.
Figure 13-2 illustrates the picking apart of
command. There is a “bug” in this code. Can you
spot it? Can you fix it?
Searching an Array Using the Linear
Search. Searching is a technique used in many
different computer programs. More often than not,
it is a list that is searched. This is the case in
Adventure 2. There are many kinds of search
techniques, but I shall concentrate on one of the
command ~~~¥P ‘carry A key”
p:=pos (‘A’, command);
command —~ 8 Carty A key”
ws
head := copy (command, 1, p-1);
simplest, the linear search.
The general technique of linear search as-
sumes the existence of a linearly ordered collection
of items. In this case the collection is the list of
strings represented by the array cmdnames.
Any collection of “things” organized in a lineup
of some kind may be subjected to linear search.
Here are some examples from real life:
w A pile of magazines on a coffee table.
w A bin of watercolor paintings on sale at the art
gallery.
m A pile of essays waiting to be graded by an
English teacher.
g A collection of recipes on 3x5 cards.
tail := copy (command, p+1, length (command)-—p);
command —~~w ‘carry A key”
~~ length (command)—p
1 J
head —~e “carry”
tail —w “key”
Fig. 13-2. The dissection of a command into head and tail.
121
@ The want ads in your local newspaper.
In all these examples, if you were searching for
a specific item such as last month’s issue of your
favorite computer magazine, a painting by a specific
artist, an essay by your favorite pupil, a recipe for
shrimp jambalaya, or an ad for a used printer for
your computer, you would be apt to start at the
beginning and search through the collection one
item at atime. You would look at each item to see if
it was the one you wanted, and continue until you
either found that item or came to the end of the
collection.
In some cases, you might take advantage of
extraneous information to speed up your speech.
For example, you might remember the color of the
cover of the magazine and limit your search to
magazines whose covers were of that color. You
might look for a painting that had the recognizable
style of the specific artist you were interested in.
You might look for certain key words in the news-
paper ad, such as printer or computer.
Because you are human, you have sophisti-
cated pattern matching abilities with which a com-
puter cannot yet compete. A computer, searching a
list of items, is not able to use such cues in most
cases. It has to take each item in turn to see
whether or not it is the one being sought. This is
always true when the list being searched has no
other structure than that of a list. Our arrays are
simply big unsorted piles—like a collection of
twenty years’ worth of LIFE magazines, well shuf-
fled from use.
Here’s how to search:
1. First ask whether or not there are any more
items left in the pile you are searching. If the
answer is yes, continue the search by doing step
2. If the answer is no, stop the search.
2. Isthe next item in the pile the item for which you
are looking? If it is, you have succeeded, so quit.
If it is not, do step 3.
3. Put aside the item you just examined and re-
jected. Continue the search from step 1.
The following easy-to-remember names can
122
be given to the three steps in the above procedure:
1. TEST, 2. COMPARE, and 3. LOOP. I shall refer
to these steps in the following discussion.
Arrays of Strings and String Compari-
son. To be able to perform searches of string ar-
rays, you must be able to compare one string to
another. For cmdlookup, you must be able to com-
pare strings in cmdnames with the string in head.
UCSD Pascal allows you to make comparisons of
string variables for equality and inequality:
S1 = S2 is true <=> S1 and S2 contain the same
character string.
S1 <> $2 is true<=>S1 and S2 differ in at least
one character position.
It is also possible to compare two character
strings lexicographically. This is a fancy word for “in
dictionary order.” The mathematical comparison
symbols < and > are used to mean the following:
S1 < S2is true <=> The string in S1 would come
before the string in S2, if
both strings were in the
dictionary.
S1 > S2 is true <=> The string in S1 would come
after the string in S1, if both
strings were in the dictio-
nary.
The search in cmdlookup only uses the comparison
for inequality.
The test for the completion of a linear search,
“Are we out of items to consider?” is not conceptu-
ally part of the search itself. It seems like unwanted
extra baggage. You will soon see that it really is.
Suppose you knew ahead of time that what you
were searching for definitely was one of the items in
the collection being searched; or to put it another
way, suppose you knew at the start that a successful
search was guaranteed. Then the TEST part of the
procedure would be superfluous. You might think
the whole search would be superfluous! Leaving
that issue aside for a moment, let’s see if we can
“Ordinary”
array
locations
} Extra location at
the end of the
array to hold the
sentinel value.
think of a way to guarantee that all your linear
searches have happy endings.
Figure 13-3 gives the basic idea—an extra
location in the search collection. Now why would
you want to increase the number of items to be
searched through? To guarantee success, of
course. You will always use the extra location to
store a “copy” of the item you are looking for. Then
if that item turns out not to be in the collection
proper, you will still find it in the extra location at
the end. You won’t have to worry about testing to
determine whether or not any items are left. At the
very worst, you will find what you are looking for
just before you run out of items to consider.
The extra item added to the collection is
known as a sentinel. It “stands guard” against the
possibility of failure.
You now have a slightly different problem to
solve with your search. There are now two possible
ways to “succeed.”
1. Find the sentinel.
2. Find the item you are searching for before
reaching the sentinel.
Fig. 13-3. A picture of an array set up for
the sentinel search technique.
In the first case, even though you succeed in
one sense, you fail in the larger sense. Case 2 could
be dubbed “real success.” How do you know if you
“really” succeed? Simple! After you succeed (which
you know you will, because you have a sentinel),
you check to see if you are at the sentinel location. If
you are not, then you really did succeed. If you are
at the sentinel you were only helped over the finish
line. Real success awaits in some future search.
You may have been wondering why nocmd
was one of the identifiers in the enumerated type
cmds. After all, you probably couldn’t imagine
typing nocmd as a command. It is there as a place
holder for the sentinel that will be used in the linear
search in cmdlookup. The cmdlookup function
guarantees the success of the linear search by
storing the string contained in head in cndnames
[nocmd]:
cmdnames[nocmd] := head;
The search is a very simple while loop:
WHILE head <> cmdnames[Icmd] DO
123
Icmd := succ (Icmd)
(* END DO *);
When this loop terminates, the value stored in
Icmd will either be the value of cmds correspond-
ing to the command the player typed or the value
nocmd.
The succ Function. In order to cause Icmd (a
variable of type cmds) to take on the “next” value of
its enumerated type, the intrinsic function Succ is
provided. Its action corresponds to picking the next
identifier from the list of identifiers given in the
declaration of the enumerated type. It is analogous
124
to saying x := x + 1 for a variable x of the integer
type. For example,
succ (carry) = drop
succ (tally) = push
succ (eat) = nocmd
succ (open) = unlock
The succ function is undefined for the last
element in an enumerated type. For cmds, succ
(ncmd) is undefined. An attempt to use succ
(nocmd) in an expression will cause an error when
that expression is evaluated at run time.
Carry and Drop: Pascal Sets
In the last chapter I discussed the overall im-
plementation of command processing in Adventure
2. In this chapter I will look more closely at two of
the commands themselves: carry and drop. Both of
these commands make use of the Pascal concept of a
set.
The Pascal language allows for variables of
type set. Very few languages allow the programmer
to create and manipulate sets. This is somewhat
surprising, because the idea of a set is a common
one in the real world and is often used in programs. I
shall discuss the concept briefly below and show
how it is simulated in languages that do not support
it directly.
WHAT ARE SETS AND HOW CAN THEY BE USED?
The concept of the set may be given a very
precise mathematical definition. I will not subject
you to sucha treatment, however. Let us define set
by giving some synonyms: collection, group, and
aggregate, etc. Try to understand set by consider-
ing some examples:
g A stamp collection.
w A set of tools.
g@ A set of silverware.
g A collection of books.
Any group of similar items may be considered to be
a set.
Sets occur in many natural situations. In our
adventures, the collection of objects that the player
is carrying at any given time is a set. It turns out to
be very easy to represent this in Pascal. In other
languages, it is not so easy. Let us briefly consider
how a set can be represented in BASIC.
Sets in BASIC
In adventure games, the player can pick up and
carry various objects. Consider the problem of
keeping track of which objects are being carried.
First of all, you need to represent the objects them-
selves. In Pascal, this is best done by an enumer-
ated type:
object = (lamp, treasure, key, sandwich, bot-
tle, shovel, noobj);
125
In BASIC, there is no best or natural way to
represent the objects. One straightforward way is
to choose numeric variables whose values are
similar to those that might be assigned to the iden-
tifiers in the Pascal enumerated type:
LA = 1
TR =2
KE = 3
SA = 4
BO = 5
SH = 6
NO = 7
Most BASIC interpreters will allow you to
spell out longer variable names, even though only
the first two characters are significant:
LAMP = 1
TREASURE = 2
KEY = 3
SANDWICH
an |
Now, how do you represent the set of objects
that the player is carrying? A simple way is to use an
array:
DIM CARRY(7)
Each entry in the array corresponds to one
object. The value of the array entry represents
whether or not that object is being carried. The
value of 1 means that the object is being carried, the
value of 0 means that the object is not being carried.
The collection of values stored in the array repre-
sents the set of items being carried by the player.
For example, if CARRY(6) = 0, the player is
not carrying the shovel. If CARRY(1) = 1, the
player is carrying the lamp.
Storing several values, each of which is either
0 or 1, in an array is wasteful of memory. Another
technique for representing a set using Os and 1s is to
use a bit string instead of an array. A bit string is
126
nothing more than an ordinary whole number
thought of in its binary form. The Os and 1s in the
binary numeral for the number may be used in the
same way the Os and Is in the array are used.
In this simple example, you can get by with a
number between 0 and 63 to represent any possible
collection of the 6 objects. The “object” noobj is not
something the player can carry. It is used in a
fashion similar to that of nocmd in the last chapter.
Thus, it can be ignored when you consider the set
representation problem.
Each object is assigned to a particular power of
two between 1 and 32:
LAMP <===> 1 = 2 to the Oth power
TREASURE <===> 2 = 2 to the 1st
power
KEY <===> 4=2 to the 2nd power
SANDWICH <===> 8 = 2 to the 3rd
power
BOTTLE <===>16 =2to the 4th power
SHOVEL <===> 32 = 2 to the 5th
power
Then, each number between 0 and 63 can be
represented by adding together some collection of
these powers of two. The number obtained by
adding the values assigned to a given collection
then represents the situation in which the player is
carrying the objects that correspond to the powers
of two chosen. For example, if the player is carrying
the key, sandwich, and shovel, the corresponding
number would be 4 + 8 + 32 or 44. If, the player is
carrying the lamp and the bottle, the corresponding
number would be 1 + 16 or 17. If the player is
carrying nothing, this is represented by the number
0. If the player is carrying all six objects, this is
represented by the number 1+2+4+8+16+32 or
63.
Using bit strings in BASIC would require spe-
cial code to calculate and interpret the various num-
bers. All this is very cumbersome and I will not
carry our treatment any further. In fact, UCSD
Pascal (and many other Pascal systems as well)
uses bit strings to represent its set variables, but it
handles all the interpretation of them automatically.
The programmer can think in terms of the set itself
and in terms of concepts naturally allied to sets.
In Adventure 2, you find the following type
declarations:
objects = (lamp, treasure, key, sandwich,
bottle, shovel, noobj);
collection = SET OF objects;
This is so “natural,” so close to the way we
think about the real situation that it takes some time
for us to realize that this is programming language
code. A collection is exactly that —a set of objects.
The enumerated type objects represents the ob-
jects in terms of their names, not as numbers or
powers of two. Set variables may be thought of
directly in terms of the set concept. The program-
mer may ignore all problems of representation.
Set Variables Used In Adventure 2
There are two collection variables declared in
Adventure 2. One is a scalar variable called stash.
stash is the set of objects that the player is carrying
at any given time during the game. The other vari-
able is an array of sets called whatshere. This array
is indexed by the enumerated type rooms. An entry
in this array corresponds to the set of objects that
happens to exist in a given adventure location at any
time. Thus, whatshere[start] represents the ob-
jects that are at the start location. whatshere[m19]
represents the objects that are at location m19 (the
nineteenth room in the maze).
The Use of Set Variables: pcarry and pdrop
As the adventurer moves around, he will issue
commands to carry and drop objects. The adventure
guide notes which objects are at which locations as
those locations are visited. This cues the adven-
turer that carrying some object is possible. For
example, at the start location the guide says:
There is a lamp here.
The adventurer may in turn say:
carry lamp
If the player wishes to leave an object some-
where, he or she can use the command drop. Of
course, you cannot drop something you are not
carrying.
The pcarry and pdrop procedures contain the
Pascal code that manage the carry and drop com-
mands. They make use of Pascal capabilities re-
garding set variables. In addition, the initialize pro-
cedure determines the original values for all the set
variables in the program. In particular, the variable
stash must initially be the empty set, that is, the
collection of no objects at all. This initialization is
accomplished by the Pascal assignment:
stash := [ ];
In general, set constants are represented by lists of
items inside square brackets:
setvar := [item1, item2, item3];
In addition to simple items, you may also use the
Pascal subrange notation in order to include several
items in a set constant. For example: suppose
mazerooms was a variable of type SET OF rooms.
Then the assignment:
mazerooms := [m1 . . m19];
would assign to mazerooms the set of rooms con-
sisting of m1, M2, M3, m4, m5, m6, m7, m8, m9,
m10, m11, m12, m13, m14, m15, m16, m17,
m18, and m19. The subrange notation m1 ..m19is
more compact for this purpose than listing all the
individual rooms themselves.
In addition to setting stash to [ ], initialize also
determines where each object is by assigning ob-
jects to various whatshere entries:
whatshere[start] := [lamp];
whatshere[coldroom] := [shovel];
whatshere[narrow4] := _ [key];
whatshere[deadend] := [sandwich];
whatshere[mudroom] := __ [bottle];
127
Notice that there is no assignment:
whatshere[m19] := [treasure];
This is because the player cannot see the treasure
until certain problems have been solved. I will dis-
cuss this further in the next chapter.
The pcarry Procedure. The pcarry proce-
dure makes use of two subsidiary functions,
objlookup and ckobject. When the player types a
carry command, there is an object named after the
verb carry: carry key, carry treasure, carry sand-
wich, and so on. The Pascal code must analyze the
command string. It must determine whether or not
the string stored in tail (see the preceding chapter
for details of how tail is determined) corresponds to
one of the objects in the game. This is accomplished
by another linear search with the sentinel al-
gorithm, just like the one used by cmdlookup. See
the last chapter for a detailed discussion of this
algorithm.
The objlookup function uses an array Obj-
namesfobjects], which is to objects what the array
cmdnames was to commands. That is, objnames
contains strings of all the names of objects in the
game. These strings are used in comparisons with
the variable tail. The code in objlookup is strictly
analogous to that in cmdlookup. The value re-
turned by objlookup is a value of the enumerated
type objects. This includes the possible value
noobj. If objlookup returns noobj, it means that the
string in tail is not the name of one of the objects in
the game. This can happen, for example, if the
player types the command carry beans or carry
knife.
The function ckobject simply checks to see
whether or not the object requested by the player is
in the current location. This is accomplished using
the Pascal set operator IN.
In general, if S is a set variable and O is a
variable whose value is one of the possible “items”
in the set, the Pascal expression
OINS
is either true or false depending on whether or not
128
the particular item represented by the value of 0
happens to one of the items stored in S. So for
example,
key IN [shovel, lamp] is FALSE
treasure IN [sandwich, shovel, treasure] is
TRUE
noobj IN [lamp . . shovel] is FALSE
ckobject uses the expression
it IN whatshere[location]
where, it is the particular value of type objects
determined by objlookup. Because the value noobj
can never be one of the values in any of the
whatshere entries, the ckobject expression will
always be false if the player requests a nonexistent
object.
If ckobject (it) turns out to be false, pcarry
informs the player
| don’t see any “it” here
where, of course, it is filled in with the specific
object the player requested. This can either mean
that the object is not at that particular location (even
though it does exist) or that the object simply does
not exist. The player can’t tell which is the case
merely by reading the message. That is, if the
player says
carry baloney
and the guide replies:
| don’t see any baloney here.
the player cannot tell whether or not any baloney
exists in the game. All that is revealed is that there
is no baloney at this particular location.
If, on the other hand, ckobject (it) is true, the
object not only exists, but is present at the player’s
current location. This means that the carry com-
mand should be “carried out.” This involves
changing the value of two different set variables;
stash and whatshere[location]. For this purpose,
there are two Pascal operators available for adding
an object to a set or for removing an object from a
set: + and —. The specific Pascal statements used
are stash := stash + [it];, which adds the object it to
the collection represented by stash, and what-
shere[location] := whatshere[location] — [lit];,
which removes the object it from the collection
represented by whatshere[location]. These as-
signments reflect the real situation being modeled:
the player is adding the object to the collection
being carried, and the same object is being removed
from the location being visited.
The pdrop Procedure. The pdrop proce-
dure is quite similar in spirit to pcarry. It calls
objlookup to determine the object requested. It
checks to see whether or not the player is carrying
that object:
IF NOT (it IN stash)
If the player is not carrying the object, the guide
replies:
You are not carrying any ‘it.’
Otherwise, the object is removed from the player’s
collection:
stash := stash — [it];
and added to the collection of objects at the current
location:
whatshere[location]: = whatshere[location]
+ [it];
129
Problems in Adventure 2
Adventure 2 contains the first examples of real
problems. In this chapter, I will present techniques
for representing problems and handling their solu-
tions within the play of the game.
In Chapter 3 I began our discussion of adven-
ture game problems. There I covered general
characteristics of problems from the players’ point
of view. I talked about clues and hints. I delved into
the topics of problem difficulty and repeatability. I
discussed the use of logic in problem solution and
the element of surprise.
Now is the time to think about problems more
from the perspective of implementation. What are
the key features of problems and how do you “map”
them into Pascal code?
EVENTS
Many problems revolve around the simple
concept of an event. An event is just that—
something that may or may not occur. An event may
happen once or it may represent a situation that may
flip-flop. For example, the act of carrying some-
130
thing is an event. The act of dropping the same thing
is the opposite of the same event, that is, the act of
carrying has not occurred if the act of dropping has
occurred. This is an example of an event that re-
flects the current situation.
Some other examples of events are
@ The player has not found the treasure.
@ The player is or is not in a specific location.
@ The player has or has not dug a hole.
@ The ogre has or has not been awakened.
@ The ogre has or has not eaten the player.
and so on. The occurrence of an event may be
recorded by setting a Boolean variable to the value
true. The same variable may have the value false if
the event has not occurred. This approach can be
used for the events that are really situations as
well. One side of the situation (the player is carry-
ing the treasure) may be represented by the value
true, while the other side (the player is not carrying
the treasure) may be represented by the value
false. This is basically the technique used in Ad-
venture 1.
BOOLEAN EXPRESSIONS AND EVENTS
If you can use Boolean variables to represent
simple events, what kind of code can you use to
compute values for those variables? The answer is
that you can use Boolean expressions. Boolean ex-
pressions are Pascal expressions whose values can
be reduced to either true or false. Boolean expres-
sions may also be used to represent more compli-
cated events that depend on more than one situa-
tion.
Numeric Relationships
Two numbers may be compared using any of
several relational operators:
turns = 0
turns <>0
turns < 25
turns > 75
turns <= 100
turns >= 50
V
VAVAA I
In all these examples, the variable turn is compared
to some number. Other examples might compare
the values of two variables. In all such examples,
the result of the comparison is true or false: either
the two values being compared stand in the re-
lationship used or they do not.
Numeric relationships may also be used to
represent events or to “compute the value of an
event.” For example, the relationship
turns >= 75
could represent the situation in which the player’s
lamp runs out of energy or the player runs out of
time.
Set Relationships: Set Membership
The Pascal operator IN yields a result which is
either true or false. IN is a binary operator (not
symmetric). The first operand of IN is a possible set
member, one of the elements that could be in the
set represented by a given set variable. The second
operand of IN is a specific set variable. The result of
the set membership relationship: O IN S is true if
the element represented by the value of O is an
element of the set that is the present value of S.
I have already discussed sets extensively in
the previous chapter. You have seen how the set
membership relationship may be used to represent
the event of carrying or not carrying an object, for
example, key IN stash or treasure IN whats-
here[start].
Equality for Enumerated Types
A special case of the = (equals) relationship
occurs for variables whose type is an enumerated
type. An identifier of the enumerated type may be
used as one of the operands of an = operator. The
other operand will be a variable of the enumerated
type. This is used frequently in adventure games in
Pascal; for examples location = start, it = trea-
sure, and so on. Again, these relationships repre-
sent events in the ways that have just been discus-
sed.
Building Boolean Expressions: Boolean Operators
The relationships just explained, plus simple
Boolean variables, are the building materials of
Boolean expressions. The nails and fasteners that
stick these materials together are called Boolean
operators. The Boolean operators are AND, OR,
and NOT.
The Boolean operators are used ina way that is
similar to the way the same words, (and, or, and
not) are used in making declarative statements in
ordinary English. In such a context, the words and,
or, and not are usually referred to as logical connec-
tives. This is the grammatical equivalent of the
Pascal term Boolean operators.
Think about the declarative statements
It is raining.
It is cold.
I am a millionaire.
The stock market went up today.
Apples are red.
131
A Buick is an automobile.
and so on. Such statements are either true or false.
In ordinary speech, people compose more complex
declarative statements by combining such simple
statements using and, or, and not:
It is raining and it is cold.
It is snowing or it is hot.
It is not raining.
It is sunny and either the stock market went up
today or the New York Yankees lost a baseball game
today.
The truth or falsity of such statements depends
on the truth or falsity of the simple statements
involved and the particular connectives used. For
example, the statement
I weigh 175 pounds and I weigh 200 pounds
is always false. But, the statement:
I weigh 175 pounds or I weigh 200 pounds
may sometimes be true and other times be false.
The use of ov instead of and changes the way in
which the truth or falsity of a statement is deter-
mined.
In Pascal, the relationships and Boolean vari-
ables are the analogues of simple declarative
statements. They may be combined to form expres-
sions, which are the analogues of more complex
statements:
(turns > 0) AND (location = start)
(treasure IN stash) AND (NOT eaten)
NOT (treasure IN whatshere[location])
Notice the difference in syntax. Pascal does not
read exactly like English. The two major things to
watch for are
@ The use of parentheses.
@ The prefixing of NOT.
In general, it is a good idea to put parentheses
132
around each operand of an AND, OR, or NOT
operator. There are times when they may be omit-
ted, but their proper use never hurts. For example,
the expression
turns > 0 AND location = start
will cause Pascal to issue a syntax error message,
while
(turns > 0) AND (location = start)
is correct.
The technical details regarding this matter in-
volve the concept of operator procedure. That is a
topic for a textbook dealing strictly with Pascal
language rules and regulations. Consult your fa-
vorite manual of this type for those details. Mean-
while, if you imitate the code in the sample adven-
tures and use parentheses liberally, you should
avoid most problems with syntax errors.
PROBLEMS IN ADVENTURE 2
To conclude this chapter, I discuss the specific
problems of Adventure 2 and their Pascal encoding.
In each case, I shall describe the general problem in
English. The description is from the point of view of
the adventurer. Then I give a set of preconditions
and postconditions. The preconditions describe
what must take place in order for the player to be
able to solve the problem. This includes events that
must take place and commands that must then be
issued. These are generally described in Pascal,
with English comments when deemed necessary.
Problem 1
You must go to the island in order to read the
message.
Precondition
location = island
The preconditions in this problem are fulfilled
merely by the player arriving at the island. The
pisland procedure is not invoked unless the pre-
condition is met.
Postcondition
readmsg = TRUE
The code in pisland causes this to occur.
Problem 2
You must find and carry the shovel in order to
later be able to dig for the treasure.
Precondition
location = coldroom
command issued is “carry shovel”
Postcondition
(shovel IN stash) = TRUE
The support procedure pcarry, of course, guaran-
tees that this is the case.
Problem 3
You must dig three times in order to find the
treasure chest.
Precondition
(location = m19) AND (shovel IN stash)
AND readmsg
command is “dig”
Postcondition
The integer variable hasdug is one larger than
before.
Each time the preconditions of this problem are met
(including the issuance of the dig command, the
value of hasdug is increased by 1.
Problem 4
If you dig four times, you fall into the flames.
Precondition
hasdug = 4
Postcondition
cooked = TRUE
If you keep on digging, you eventually dig a hole
into the flames room. You get a warning after the
third time you dig. By that time, you have been told
that a chest has been uncovered. If you are greedy
and keep digging, you get “toasted.”
Problem 5
You must find the key and carry it in order to
open the treasure chest.
Precondition
location = narrow4
command is “carry key”
If you arrive at the treasure location without the
key, you will be unable to open the chest when it is
uncovered. Of course, you can always go back and
find the key after you have dug up the treasure, but
this may take more turns.
Problem 6
You must push the treasure through the crack
between narrow1 and the vestibule.
Precondition
location = narrow1
treasure IN whatshere[narrow1 ]
command is “push treasure”
Postcondition
treasure IN whatshere[vestibule]
The treasure is too heavy to carry up the ladder.
The only other way to get it back to the start is to
get it into the vestibule by means of this command.
This is the most difficult problem in Adventure 2.
133
There is a hint about what to do. If you visit the treasure IN whatshere[vestibule]
location deadend, you will be able to read the location = vestibule
message “Good things go through small places.” command sequence is “carry treasure” and
then “up”
Problem 7
You must carry the treasure to the start loca- Posteoudition
tion and drop it there in order to win.
Precondition treasure IN stash
134
Other Techniques Used in Adventure 2
In this chapter I discuss miscellaneous techniques
used in Adventure 2. The approach is similar to that
of Chapter 10. You should read the explanations
here and then study the relevant sections of code in
the listing of Adventure 2. This is a chapter to be
dipped into at random and over and over again.
COUNTING TURNS
The counter turns keeps track of the number of
turns a player has taken. If you look through the
code, you will see that it is only changed in one
place, namely in the whichway function. What this
means is that the player takes one turn for each
direction or travel command given. Other com-
mands, suchas, carry, drop, eat, or dig do not count
as turns. This is a somewhat arbitrary decision. It
gives the player slightly more time to play. On the
other hand, it is not the most liberal policy of
counting I could have adopted. I could have counted
only those travel commands that succeed. A good
exercise is to change the code to use this more
liberal rule. See if you can figure out how to do it. If
you like that rule better, then put it in!
DISPLAYING THE CONTENTS OF A SET
The procedures pinvent and showobjects
both cause the contents of a set variable to be
printed out. This involves the use of the string array
objnames, which was discussed in Chapter 13.
The technique is simple: the IN operator is
used to check for the presence or absence of each
possible item. This can be done in a FOR loop:
FOR lobj := lamp TO noobj DO
If an object is present in the set, its identifier is
printed out using the objnames array:
write (objnames[lobj]);
The pinvent procedure goes through the set vari-
able stash in this fashion. The showobjects proce-
dure goes through the variable whatshere[loca-
135
tion]. The latter clearly depends on where the
player happens to be at a given time.
SIMPLIFICATION OF TRAVEL
Adventure 2 contains a new procedure called
travel, which simplifies the changing of location
during the play of the game. It replaces most of the
case statements that were used in Adventure 1 for
that purpose.
The travel procedure takes six parameters of
type rooms:
PROCEDURE travel (
nloc,
sloc,
eloc,
wloc,
uloc,
dloc: rooms);
A typical call of travel might look like
travel (deadend, batscave, nowhere, nar-
row2, nowhere, maze);
which means that from the current location (which
happens to be the location steam), the possible
destinations are deadend going north, batscave
going south, nowhere going east, narrow2 going
west, nowhere going up, and maze going down.
The travel procedure uses a nested procedure
called newloc. The newloc procedure checks the
requested destination. If it is nowhere, it prints the
message “There is no way to go in that direction.” If
it is not nowhere, it changes the value of the vari-
able location and sets the Boolean variable chgloc
to true—I’ll say more about chgloc shortly.
The travel procedure uses a case statement
that resembles those used in Adventure 1. This
case statement uses a call to newloc for each case
label:
CASE whichway OF
n: newloc (nloc);
s: newloc (sloc);
136
: newloc (eloc);
: newloc (wloc);
: newloc (uloc);
: newloc (dloc);
aczo
END (* CASE whichway OF +);
The travel procedure is a more compact way to
handle directional commands. It removes the need
for many of the location procedures that were used
in Adventure 1. The rule is that if a location has no
special case code, there does not need to be a
corresponding location procedure for it. A location
such as ladder still needs to have a separate case
statement to handle travel. This is true because the
ability to go in the up direction depends on whether
or not the player is carrying the treasure. This
conditional travel is not handled by the code
u: newloc (uloc);
in travel.
The case statement
CASE location OF
in the main program block of Adventure 2 now
invokes travel in most cases (24 out of 33). This
makes the program both smaller and easier to un-
derstand.
In Adventure 1, the description of a location is
repeated as long as the player stays there. This can
get annoying, especially if the description is long.
Adventure 2 solves this problem. The description
of a location is given only when the player enters
the room. If the player then issues commands and
stays in the room, the description will not be dis-
played again. This is accomplished by using the
Boolean variable chgloc; chgloc is set to false by
the procedures noway and docommand. It is set to
true by newloc and by various location procedures
whenever the players’ location has actually
changed. The show procedure checks the variable
chgloc to determine whether or not to print the
description.
The player may explicitly request that the de-
scription be repeated by giving the command look.
The plook procedure saves the current value of
chgloc, and temporarily sets chgloc to true. It then
calls show for the current location. After show
prints the description, plook sets chgloc back to its
old value (which may be either true or false).
THE USE OF FILE VARIABLES
Most of the descriptions of locations used in
Adventure 2 come from a disk file. This saves space
in the program allowing more locations and more
problems. It also causes the printing of descriptions
to be slower. However, this seems to be a reason-
able tradeoff.
Placing descriptions in a file requires the use
of a subsidiary program that I call MAKEDESC
(short for MAKE DESCriptions). MAKEDESC
reads a text file of descriptions and creates a
database of descriptions for use by an adventure.
The database consists of two files, an index file and
a descriptions file.
The construction and use of MAKEDESC are
discussed in great detail in several chapters begin-
ning with Chapter 18. Included in those chapters is
information on how to use MAKEDESC in writing
your own adventures, as well as technical discus-
sions of MAKEDESC and the support code needed
for using the output from MAKEDESC.
SCORING YOUR ADVENTURES
People play adventures for many reasons.
They want to solve the problems posed, they want
to find the treasures, and they want to “win.” The
definition of win is usually “accumulate the highest
possible score.”
A player may gain points for a number of ac-
complishments including
@ Solving problems in the adventure.
@ Visiting each location in the adventure (a certain
number of points for each location visited).
@ Visiting all locations in the adventure (bonus).
@ Finding the treasure(s).
mw Bringing the treasures back to a safe place.
In addition to points awarded for positive ac-
tions, there may be points subtracted from the total
score for various other reasons:
w Asking for help or hints during the play of the
game.
@ Not avoiding various dire actions including:
Waking the ogre (or other monsters).
Being killed and having to be reincarnated.
In Adventure 2, I have devised a scoring func-
tion that takes such various factors into account.
Here’s the way I have it now:
@ 5 points are awarded for each location visited.
@ 140 points are scored for getting the treasure
back to start.
@ 50 points are deducted for being cooked; that is
falling into the flames.
@ 50 points are deducted for being eaten by the
ogre.
@ 25 points are deducted for waking the ogre.
THE HELP COMMAND
Adventure 2 contains a help command. If the
player asks for help, the guide will respond by
giving hints. If the player has already read the
message on the island, the guide will tell the player
that the treasure is in the maze. Otherwise, the
guide will tell the player where hints may be
found—namely, near the lake and in the alcove.
The scoring rules in this adventure do not
punish the player for asking for help. You might
want to modify Adventure 2 so that a few points are
deducted if the player ever asks for help. This
would require another Boolean, variable (like
cooked or quit), which would be false initially, but
which would be set to true if the player asks for
help. The score function may then examine this
variable to see if points should be deducted or not.
THE LAMP AND THE LIGHT COMMAND
Adventure 2 requires that the player carry the
lamp found at the start. The lamp must also be lit.
Otherwise, the player falls into a pit and is killed
after a few turns. One of the first problems the
137
player must solve is how to treat the lamp properly.
The light command checks to make sure that
the player has the lamp by using the set relationship
IN:
lamp IN stash
If this is true, the lamp will be lit if the player says
light lamp
or, simply
light
The latter is a convenience for the player. Many
adventure games have lamps that need to be lit. In
other games, it is possible to use a variety of com-
mands in order to light the lamp: lamp on, lamp,
light, and so on. I have only provided two: light
lamp and light. You might want to modify Adventure
2 to add further commands that cause the lamp to be
turned on.
The cklamp procedure is provided to monitor
the status of the lamp. It counts the number of turns
the player spends underground with the lamp
turned off. If this gets too large, the game is ended.
It warns the player whenever the lamp isoff. It also
heavy-handedly turns the lamp off automatically
when the number of turns taken gets too large. This
forces the player to finish up the game in a certain
amount of time in order to win.
THE DIG COMMAND
When the player gets to the right maze loca-
tion, it is time to dig for the treasure. Provided that
the player has located and is carrying the shovel,
and has read the message on the island, the com-
mand to dig will be obeyed. However, it is not
enough to simply dig once—you have to keep on
digging. In fact, you must dig at least three times
and no more than four times. Each time the dig
command is given, a new message is conveyed to
138
the player. This is controlled by the variable has-
dug, which counts the number of times the player
has used the dig command.
THE EAT COMMAND
The eat command has been added to Adven-
ture 2 strictly for fun. There is a sandwich that may
be found. If the player is carrying the sandwich and
says “eat,” the guide replies, “Oh, yummy!!” Other
messages are given, depending on the circum-
stances. However, the sandwich and the eat com-
mand play no role in the scoring of Adventure 2.
THE OGRE
The procedures pogreroom and ogreaction
handle the ogre character. The ogre is strictly bad
news—nothing good is associated with the ogre. If
the player visits the ogreroom (which must be done
in order to get the maximum score), there is a
chance that the ogre might wake up. If the ogre
wakes up, there is a chance that he might catch and
eat you. If you should ask to go down while you are
in the ogreroom, you find out that you have stum-
bled into the ogre’s cooking pit. All of these unde-
sirable happenings cause you to lose points.
Adventure 2 uses a very crude technique to
decide whether any of these things occur. The
number of turns is examined. If it is an even multi-
ple of 3 (3, 6, 9, 12, and so on), the ogre is
awakened. If the ogre is already awake and if the
number of turns is an even multiple of 2, the player
is eaten. The Pascal MOD function is used to de-
termine these conditions:
(turns MOD 3) = 0
or
(turns MOD 2) = 0
This technique could be slightly improved by using
a random choice. This would depend on the avail-
ability of a random number generator.
UCSD Pascal Development Techniques
In Chapter 4 I discussed UCSD Pascal and some
elementary techniques for its use. In this chapter, I
wish to elaborate on those topics. In particular, I
discuss how best to use your UCSD system in
writing large adventure games and other Pascal
programs.
MANAGING YOUR FILES EFFECTIVELY
Most personal computer systems equipped
with UCSD Pascal use floppy disks as mass storage
devices. Your files all “live” on various floppy
disks. As the number and size of the files you have
created grows, the problems of managing them
grow accordingly. You have to worry about running
out of space on a given floppy. You have to worry
about sharing space with various UCSD system
files. You have to worry about keeping track of
various versions of your files. In short, you need to
worry about more than just writing Pascal pro-
grams.
Organizing Your Source Files
There are a number of problems associated
with writing a large program in UCSD Pascal. I
have already hinted at some of these. Now it is time
to be more specific.
UCSD Editor Limitations. The UCSD Pas-
cal full screen text editor requires that the entire
file being edited fit into memory. Unfortunately,
Pascal programs tend to exceed this limit. That
implies that there will be one of two results:
@ You never write Pascal programs whose source
files are bigger than what the UCSD editor can
handle.
@ You use techniques for splitting Pascal programs
into more than one source file.
The first alternative needs no comment. The
programs in this book necessitate the use of the
second alternative.
Each implementation of the UCSD system im-
poses its own limitation on the size of an editor file.
The Apple Pascal limitation is approximately
18,400 bytes, which is about 38 disk blocks. In
order to leave room for expansion and for fixing
139
bugs, it is good to impose a smaller maximum size
for each source file. 15,000 bytes seems like a
reasonable choice: it is easy to remember and it
allows for expansion.
_ When you start writing an adventure game,
you should monitor the size of the source file. In the
UCSD editor, you discover the current size of your
file by using the set environment command. This
command gives you a screenful of information about
the file you are editing. Among other things, it tells
you the number of bytes used and the number of
bytes remaining in the file. When the number of
bytes used shows about 15,000 (or whatever limit
you decide is reasonable on your system), it is time
to start anew file. When to start a new file may also
be based on the logical divisions of the program. In
other words, it is silly to split a source file in the
middle of a procedure just to adhere to the size
limit.
As an example of this technique, the following
summary shows the source files used in Adventure
2 and the sizes of each:
File Name Size
a2. data. text 2671 bytes
a2.ul.text 5836 bytes
a2.u2.text 10662 bytes
a2.m2.text 4096 bytes
a2.maze. text 2749 bytes
a2.main.text 3499 bytes
As you can see, none of these files comes close to
our self-imposed limit. The division of the source
code for Adventure 2 into these files was deter-
mined more by the logical structure of the code than
by size. However, in the case of a2.ul.text and
a2.u2.text, size was the determining factor. These
were originally a single file. They were split when
the total size exceeded 15,000 bytes. The following
summary shows the list of files again, this time with
the parts of Adventure 2 included in each file shown
instead of the file sizes:
File Name Contents
a2.data.text The const, type, and var sections
140
File Name Contents
of the program, together with the
simple procedure wipe.
a2.ul.text | The introduction procedure through
the ckobject function.
a2.u2.text The pcarry procedure through
the travel procedure.
a2.m2.text The ogreaction procedure through
the pogreroom procedure.
The pmaze procedure.
The ppit procedure through the end
of the main program.
a2.maze. text
a2.main. text
Compiling Multiple Source File Programs
The UCSD Pascal compiler allows you to com-
pile a single program that exists in more than a
single source file. This is accomplished by the use
of the include option. The include option tells the
compiler to read more source code from another
file. The form of the option is as follows:
(*$lxyz.text«)
This is similar to other UCSD Pascal compiler op-
tions. Options are initiated by an open comment
bracket, followed by a dollar sign:
(*$
Then comes a single letter indicating which option
is being used and any other information needed by
the option itself. In the case of the include option,
the other information is the name of the source file
to be “included.” For example, in Adventure 2 at
the end of the first source file, a2.data.text, the
following sequence of include options appears:
(*$la2.u1.text*)
(*$la2.u2.text*)
(*$la2.m2.text*)
(*$la2.maze.text*)
(*$la2.main.text*)
The result is that the Pascal compiler reads through
the source files a2.u1.text, a2.u2.text, a2.m2.text,
a2.maze.text, and a2.main.text in that order, after
it reads through the file a2.main.text. The net ef-
fect is just as if all six source files had been con-
tained in a single file. When you invoke the Pascal
compiler to compile a2.data.text, the compiler will
automatically find all the other files needed. You
don’t need to learn any special commands in addi-
tion to the include option itself.
Giving the Compiler a Chance
There is another option that should almost
always be used when compiling large UCSD Pascal
programs. That is the (*$S+*) option. This causes
the compiler to go into “swapping” mode. Instead of
having all of the code for the compiler in memory at
the same time, various parts of the compiler code
are swapped in and out. Only part of the compiler
code is present in memory at any given time. The
rest of the code stays on the floppy disk.
Let us examine the consequences of either
using or not using the swapping option. What is
affected by the swapping option is the amount of
memory that is available to the compiler for its
symbol table. When the swapping option is not
used, there is a limited amount of symbol table
memory. This means that only a medium sized
program can be compiled before this memory is
exhausted. When the swapping option is used, the
amount of symbol table memory increases dramati-
cally and with it the size of program that may be
compiled. It is possible to increase the amount of
symbol table memory even more. This is done by
using the “double swapping” option (*$S+++).
This slows down the compiler, because it must do
more juggling of code into and out of memory. It
increases the symbol table space to its maximum
amount and therefore provides for the largest pos-
sible source program.
UCSD SYSTEM TRICKS AND PITFALLS
The UCSD system includes some commands
that make dealings with your files and disks easier.
These, however, should be used with caution.
The Prefix #5 Trick
The prefix command in the UCSD filer allows
you to abbreviate file names when communicating
with the UCSD system. The system automatically
uses the prefix to decide which disk volume to
search for a given file. If you set the prefix to #5,
the system will read the volume name of the disk
currently in the drive corresponding to #5. This
means that if later you wish to change the disk in
that drive, you must reenter the filer and reset the
prefix to #5 again because the name of the physical
disk has changed. Either that, or you have to type
the volume name explicitly, the very task that the
prefix command was designed to allow you to skip.
Some implementations of UCSD Pascal, Apple
Pascal in particular, allow you to use the following
trick. First enter the filer program. Then open the
door on the disk drive corresponding to unit #5.
While the door is open, issue the prefix #5 com-
mand. The disk will spin, aclicking or buzzing noise
may eventually be heard, and the filer will eventu-
ally respond by saying:
Prefix is #5:
This means that no matter what disk is in #5, the
filer will use its name as the prefix. Thus, you don’t
have to reissue the prefix command if you change
the disk in #5. The system will automatically de-
tect the change and use the new disk’s volume name
as the prefix.
The K(runch Command
As you develop a large program, the disk con-
taining its source files may need space mainte-
nance. The available space on the disk tends to
become fragmented because of the way the system
moves files around during editing and compiling.
The UCSD system provides the K(runch command
to allow files to be rearranged. This command
moves files around in order to create a single large
block of unused space. It is a good idea to examine
your disks occasionally and if necessary do a
K(runch. The question is, when should you
K(runch?
There is no hard and fast rule about when to
K(runch. However, sooner rather than later is the
best idea. To start with, you should use the
141
TARR:
FORMAT. CODE
CMDNAMES. TEXT
* UNUSED :
TABCHI2. TEXT
TABCHIS. TEXT
» UNUSED :
TABCH11. TEXT
TARCHI4A. TEXT
TABCHIS. TEXT
«< UNUSED >
TABCH14. TEXT
TABCHI4E. TEXT
FAKE. TEXT
HDR. TEXT
< UNUSED >
11/11 files,
2 24-Aug-as
118 unused,
6-Feb~82 &
7-Feb-82 26
=O)
38
42
46
66
82
116
150
180
202
212
216
220
largest
Code
Text
Text
Text
27~-Apr-8s
27-Apr-83
Text
Text
Text
7 ~-Aug-BS
24-Aug-83
Text
Text
Text
Text
24--Aug-8S
25-Aug-83
am ud
24-Aug-83
60 in
Fig. 17-1. A typical output from an E(xtended directory list command.
E(xtended directory list command to get a picture
of the current state of your disk. A typical output
from such a command is shown in Fig. 17-1.
Each line beginning with < UNUSED > ac-
counts for a block of unused space on the disk. If the
largest such blocks is not big enough to hold the
largest file on the disk, you probably should
K(runch. In the above display, the biggest available
space is 60 blocks (this information is always given
in the last line of the display: 11/11 files, 118
unused, 60 in largest). The largest files on this
disk are both 34 blocks in size. Either will fit com-
fortably in the 60 block space.
What are the consequences of not K(runching
soon enough? It may become impossible to write
out a new version of the file from the editor; or it
may be impossible to perform a compile, because
the compiler may run out of space for its output
files. In the former case, there is a way out. In the
latter case, you will have to abort the compile, do
the K(runch, and recompile. So what should you do
if the editor says
ERROR: WRITING OUT THE FILE.
PLEASE PRESS <SPACEBAR> TO
CONTINUE
142
At this point, you should remove the offending disk
from its drive and insert another that you know has
enough room (it is a good idea to have at least one
“scratch” disk available for this purpose). Write the
file to this new disk using the write command. Then
put the old disk back in, K(runch it, and use the filer
to move the saved copy on the scratch disk back to
your working disk. This is greatly facilitated if you
have used the prefix #5 command as described
earlier in this chapter.
Losing Source Files Accidentally
There are many ways that source files can get
lost. Everyone hopes that such a disaster never
happens to them. However, there are only two
kinds of programmers: those who have lost their
source code, and those who are going to. So it is
wise to have some methods for combating lost
source files.
You should always have a copy of your pro-
grams on paper. If you own a printer, this is easy
enough to accomplish. If you don’t, you should save
your pennies and get one, or you will wind up in the
second category of programmers. When you make a
listing of your program, write the date on it. Then
as you make changes, write them on the listing until
you run out of space (or you get tired). Then print a
new listing. With an up to date listing, the worst
that can happen is that you have to reenter all your
code by hand. That fate is bad enough, but the
alternative —having to write your code over again
from scratch—is far worse.
Backups on Disk. If you have the time,
energy, and the money to afford extra disks, keep-
ing backup copies of your source code is an alterna-
tive solution. Simply copy the entire disk using the
filer. Then put it in a disk box and put it away ina
cool, dark place. If you lose your primary copy, or
the cat tears it to shreds, your backup copy will save
the day.
Accidental Loss of Files. Occasionally, you
might use the write command in the editor and type
the wrong file name. If it is a new file name, there is
no problem—simply use the filer to change it to
what you originally intended. On the other hand, if
it is the name of an already existing file, you have
trouble. The file you just overwrote is destroyed by
the copy of the new file you are editing. There may
or may not be a way to recover a copy of the file that
was overwritten.
It may be that the reason you typed that par-
ticular wrong name was that you just finished edit-
ing that file. If you edit many different files in a row,
you may get names confused. This is especially true
if the names are similar: source1.text, source2.-
text, source3.text, and so on. For the sake of argu-
ment, assume that you are editing and saved a file
named source2.text. Then you edited source3.
text, but instead of saving it as source3.text, you
write it to the disk using source2.text as the file
name. What actually happens is this: the editor
finds a block of space on the disk big enough to hold
the new file; the editor writes out the new file; and
finally the editor deletes the previous copy of the
file. This last action creates a new block of
<UNUSED> space on the disk, which actually
contains the file you just “overwrote.”
How do you recover from this mistake? If you
realize the mistake right away, you have a good
chance at recovery. Enter the filer immediately.
Then use the M(ake command to create files that
will occupy the blocks of space currently marked as
< UNUSED >. For example, in the display shown
earlier, you could issue the following sequence of
commands:
MAKE WHAT FILE? TMP1.TEXT[8]
MAKE WHAT FILE? TMP2.TEXT[20]
MAKE WHAT FILE? TMP3.TEXT[30]
MAKE WHAT FILE? TMP4.TEXT[60]
This will create the four files shown, each filling in
one of the “holes” in the disk. Now edit the four files
in turn, and see if any of them happens to be the file
you just lost. If so, simply delete the remaining
files, delete the new copy of the file you lost (which
contains the wrong information), and finally rename
the tmp file to the name of the file you lost. All this
is complicated, but desperate situations call for
drastic measures.
Once you have created a file with the editor, it
already has a file name that is known to the system.
When you edit it again, use the S(ave command to
write it back out to disk, instead of the W(rite
command. This will avoid the pitfall I have just
mentioned in 99 cases out of 100.
The #*%%!!? ESCAPE Key
In the editor there is one particularly nasty
pitfall that you should know about. By realizing
what can happen, you can work to avoid disaster.
When you go into insert mode in the UCSD editor,
it is possible at any time to cancel the entire text
inserted by simple pressing the ESCAPE key. This
action is absolute: there is no escape from this
escape! Once you press the ESCAPE key, all that
beautiful text you have just typed is wiped off the
screen, thrown in the proverbial bit bucket, and
gone forever. The system does not give you a sec-
ond chance to decide if you really want to cancel
your insert or not; it just does what you “tell” it.
Notice that on the Apple II family of computers
the ESCAPE key is located on the top left corner of
the keyboard, right next to the key with the
characters ! and 1 on it. (This is on Apple II and
Apple [Je keyboards.) It is disgustingly easy to
reach for the 1—! key and accidentally hit the ES-
CAPE key instead. The moral of this story is this:
143
don’t stay in insert mode for very longbefore typing do, if it has been two hours since you started your
“C to accept the insert. Sooner or later you are _ insert, the only alternative left to you is to have a
going to hit the ESCAPE key by mistake. When you good cry.
“C is Control-C
144
Preview of MAKEDESC and BROWSE
Adventure 2 differs radically from Adventure 1 in
its treatment of descriptions. All textual matter was
embedded directly in the code of Adventure 1. In
Adventure 2, most of the descriptions have been
moved out of the program and into a disk file. The
disk file containing the text of the descriptions,
along with another file used as an index for the first
file are collectively referred to as the descriptions
database for Adventure 2. The next six chapters
present the program used to generate this kind of
database and describe the creation and use of simi-
lar databases in adventure games.
DATABASE CONCEPTS
The term database is a very general one. It is
used in many different contexts, not always in con-
nection with computers. A database can be defined
as any collection of information. More often than
not the term implies that the information itself is
organized for convenient retrieval. Subsidiary in-
formation that is not part of the collection of infor-
mation but is used solely to facilitate retrieval is
often referred to as the database index.
The databases for Adventure 2 and beyond are
textual databases. The text consists of descrip-
tions. In Adventure 2 they are descriptions of ad-
venture game locations. In future adventures they
may be descriptions of other things as well, includ-
ing descriptions of events and speeches of charac-
ters in the game.
PREVIEWS OF CHAPTERS 19 THROUGH 24
Chapter 19 contains the listings of two Pascal
programs that I call MAKEDESC and BROWSE.
The first of these is the program used to generate
the descriptions databases. The second is used to
examine descriptions databases outside adventure
games. It may be used to preview your descriptions
databases before you incorporate them into your
own games. It also serves as an introduction to the
techniques of retrieving data from databases.
Chapter 20 is called “Creating a Database of
Descriptions” and tells how to use the MAKE-
DESC program. MAKEDESC processes a text file
145
that contains a mixture of two kinds of information:
@ The actual text lines of the descriptions.
@ Instructions to the MAKEDESC program itself.
Chapter 20 tells you more about the nature of the
instructions to MAKEDESC. It also describes the
process of running the MAKEDESC program on a
UCSD Pascal system and the information to provide
to the program in response to various prompts.
The title of Chapter 21 is “Random Access
Files in UCSD Pascal.” It deals with the specific
nature of file access. Both random and sequential
files are defined and discussed in some detail. The
extensions to Pascal needed to support random
access files are described in the context of the
UCSD system. The use of the BROWSE program
for previewing descriptions databases is described.
Finally, a discussion of how to incorporate descrip-
tions databases into your own adventure games is
given. In particular, the show procedure is de-
scribed. The show procedure is used both by
BROWSE and Adventure 2 to retrieve descriptions
from the database and show them to the user.
Chapter 22 describes the structure of the de-
scriptions databases in detail. It is entitled “The
Structure of Adventure Databases” and begins with
a discussion of index files in general and the de-
scriptions database index file in particular. The
contents of the index entries are described in detail
and revealed to consist of a collection of records.
Each record in turn consists of a number of entries:
name, id, dbegin, dend, and link. The function of
146
these entries, called fields, is described.
The use of the database in an adventure game
requires that the game declare and manipulate the
database as a collection of files. The second part of
Chapter 22 describes this process in detail. Finally,
a discussion of the show procedure in the context of
adventure games wraps up the chapter.
Chapter 23 delves into the programming
techniques used in MAKEDESC. It discusses con-
cepts from the realm of computer language trans-
lators including symbol tables, hash functions and
hashing techniques, and lookup in symbol tables
(linear search and hash lookup). Each of these
techniques is discussed in general and in the con-
text of adventure games and MAKEDESC. The
advantages of hashing and hash table lookup are
explained in comparison with linear sarch tech-
niques.
The manipulation of symbol tables is explained
with the MAKEDESC code as an example. The
operations of adding entries to the table and looking
up entries in the table are both delved into. Chapter
23 merely scratches the surface. If you find yourself
fascinated by its concepts and techniques, a book on
data structures, computer language translators, or
compiler construction might be of interest to you.
The section on MAKEDESC concludes with
Chapter 24 which is entitled, “What Else Can You
Put on Disk?” It is a short and speculative chapter.
Several suggestions for extending the usefulness of
adventure game databases are given. Some of the
suggestions will be followed up in Adventure 3.
Others are offered for your enjoyment and imagina-
tion.
MAKEDESC and BROWSE
LISTING 19-1. MAKE80 DATABASE GENERATOR
; i MakeDesc (aka Make80) and Browse80
£ m a kr e d e 3 c > use a description file for input, and output
f + a database file and an index file for use
{ This program reads a file of text 3 byadventure games. See Chapter 20,
{ cantaining a mixture of descrip- } Page 166 and in particular page 171
{ tions and commands. It creates 3 for usage instructions.
{ @utput files which may be used as }
{ a database of textual passages. 3 Adventure 1 doesn’t use a database.
{ An index file cantains information }
{ about each passage: start and stop } Adventure 2’sdescription file
{ indices in the data file (which is 3} _ isin AppendixB, page 286,and
{ a file of strings of fixed length 3} should benamed“A2.DB80".
{ 80), an identifier which may be }
{ used if desired to retrieve the 3 Adventure 3's description file
{ passage by name ~ say using a hash } is in Appendix C, page 292, and
{ scheme, etc. A data file is also 3} shouldbenamed“MTADV".
{ created containing all the text *
{ lines from the input, either padded} (Foryourownadventures, the file
{ with blanks if the original line } canhaveanynameyoulike.)
{ was less than 80 characters long, 3
147
ue
or truncated ta 80 characters if
original line was toa lang.
he ke
i
i
i
i
i
|
i
i
j
j
A
}
1
|
i
i
H
!
3
Le
ts Ie Ye ir Se
FROGRAM makedescriptionss
CONST
hashmax = SOs
blank4o an
ory
TYFE
(indexsectiaon, descsection)s;
STRINGCL4OT;
whichsect
i il
pname
storyline = STRINGC8OI;
byte = 0. .2558
procs = (pnamelookup, pentername,
pdescribe, pmakename,
pfindrec)s;
placerec =
RECORD
CASE section:whichsection OF
indexsectioan: ( tableentry: INTEGER );
descsections: ( name: pname;
ids INTEGER s
dbegins INTEGER;
dend: INTEGER:
link: byte 5
END;
VAR
infile: texts
narrates FILE OF STRINGCSOT:
indexfiles: FILE OF placerec;
148
hash: ARRAYLO.,.hashmax] OF byte;
symhash: INTEGERS
where: INTEGER:
{ index TO symbol table }
places: ARRAYCO,.255] OF placerecs
dumprecs placerecs
lastrec: placerecy;
nextplace: bytes { ainit=21 }
outrecs: INTEGER:
{ index TO descriptians. init=0 }
placename: pname;
lines storylines
blank®oO: starylines
iname,
NNAME «
xXNaAamMes STRING:
is INTEGER:
ditto: BOOLEAN;
{ was last data line a ditto line? 3
tracings BOOLEAN;
che CHARS
warnsets SET OF CHARS:
{C init = C'S, 7" ,*e* 7]
rome sem ene mn ste stm ane tt ess te ne etn nnn ts an is stn ns ae en we so tee a nes mt te "Ye
{ t r a c e 3
{ This procedure allows a trace of 3
{ all procedure calls made by the 3
{ program. This is most useful in 3
{ the program debuqging phase. >
PROCEDURE trace (whorprocs);
BEGIN
CASE who OF
149
pnamelookup:s writeln ( namelookup") 3;
penternames: writeln (*entername’*)s
pdescribes writeln (*describe’ >);
pmakenames writeln (?makename’)s
pfindrec: weiteln (*findrec’)s
END ¢ CASE who 3;
END { FROCEDURE trace 343
.
it
Called by procedures on entry
checks toa see if tracing is in
effect and if so, calls the trace
procedure toa print the name of the
procedure ar function which is
being entered.
et be Le RS
bs he bs he he
i
|
1
i
i
i
j
i
!
j
exon ce en ss en es eA es of
FROCEDURE inta (wha: proacs)s
BEGIN
IF tracing
THEN
REGIN
write (’entering *);
trace (who) s
END;
END ¢ FROCEDURE into 33
Called by entername. Its job is
ta determine if a given name has
already been entered into the name
table. The technique used is to
traverse a linked list of name
entries, all of which have the
same hashvalue. The hashvalue is
mre ra eee ene A et
St ht bs bw he bs 8 he hs
150
f
5
£
‘
=
co
FUNCTION namelookup ¢
RE
END ¢
ae i he hn hn Eh hn Th ln le i
3
computed by the caller
7
thisnames
GIN
into (pnameloakup)
PlacesCOl].name := thisname;
where := hash(Csymhashids
WHILE placeslCwherel].name
DQ
wher @
{ endda
= placestwhered].link
namelookup := ( where “3 O )3
FUNCTION namelookup 33
This procedure is called in
to enter a placename in the
table. It is called whenever a
placename instruction line (line
beginning with a *$*) is scanned
in the make8o source file. It
calls namelookup to determine if
the placename has been used before.
PROCEDURE enternames
VAR
i: INTEGER;
REGIN
inta (pentername) ;
symhash := OO;
FOR i := 1 TO length
DO
symhash :=
(symhash +
(placename)
ord
and left in
the global variable “symhash’.
order
symbol
i
j
H
(placenameLlid))
!
ho ket be
pname ):
thisname
he BS Lt ke het he
ee oe
MOD
BOOLEAN;
(hashmax+1)
151
{ enddo 3
writeln ("placenames *,placename);
writeln (*hashvalue: *,symhash) ;
IF namelookup (placename)
THEN
BEGIN
writeln ("XX duplicate place:”*);
writeln (placename) 3
END
ELSE
BEGIN
IF NOT ditto
THEN
placest€nextplacel.dend := autrec -
< endif 33
il
IF nextplace =} ©
THEN
lastrec
= places(€nextplace]
{ END IF 33
nextplace := nextplace + 13
WITH placestnextplacel] DO
REGIN
id = nextplaces
link := hashE€symhashis;
dbegin = outrec;
name := placename;
hash(€symhash] := nextplacey;
ENDs
END ¢{ IF namelookup 33
ditto := false;
END { PROCEDURE entername 35
Adds a line of text to the des-
the input line to be exactly 890
this by padding the line with
blanks.
i
i
i
H
Cle te he he he ae i a
PROCEDURE describe ( thisline: storyline )3;
BEGIN
into (pdescribe) ;
IF length (thisline) «< 80
THEN
thisline :=
concat (thisline,
capy (blank@o, 1, 80 -
{ endif 33
narrate” := thislines;
seek (narrate, outrec);
put (narrate) s
outrec s= outrec + 1s
END {€ FROCEDURE describe 3;
PROCEDURE makename ( VAR thisname:
BEGIN
into (pmakename) ;
IF length(thisname) «< 40
THEN
thisname :=
concat (thisname,
criptions file. It first adjusts
characters long. It accomplishes
BY he hw net ke a es
pname)
capy (blank4oO, 1, 40-length
length (thisline))}
(thisname)))
153
writeln ("placename: *,thisname) ;
END < FROCEDURE makename 33
(re a faeces ty Senki sani sev ba-Sost) aban sith ees ao we Soon Sons cise leben va’ Geese nei voin ane iy sboud ado aoe, Se 3}
{ f i n cd r e c 3
{ }
{ Handles the ditto instruction lines}
{ from the make8O source file. It 2
{ must distinguish between the case 3
{ where a name is specified and the }
{ case in which only a ditto was 3
{ used. 3
I me a meme ee en ees este et ee a tne we est tn tee neem ter nt tn ae nm enn tt ae nts anes nen ane"
PROCEDURE findrecs
BEGIN
into (pfindrec) ;
IF length (line) = 1
THEN
BEGIN
astrec.dheqins
placesCnextplaced.dhegin 1
lastrec.dends
places(nextplacel].dend =
ce
lastrec = places(€nextplaced
END
ELSE
BEGIN
line := copy (line, 2, length (line) - 1)3s
makename (line)
symhash := O;
FOR a = 1 TO length (line)
DO
symhash :=
(symhash + ord (linelid)) MOD (hashmax+l)
{ enddo };
IF NOT namelookup (line)
THEN
BEGIN
wreiteln (error: ditto name does not exist’);
writeln (line);
154
END
ELSE
BEGIN
places(C€nextplacel].dbegin :=
placesCwherel].dbeqins;
places(€nextplacel.dend
placeslCwherel.dend;
lastrec := placesEwhered;
END { IF NOT namelookup 35
END { IF length(line)?>1 33
ditto := trues
END {£ PROCEDURE findrec 33
{-~ ce ‘sso a pmnapew ss Gennb aban shes, aes Spann eet mie om ni eet atts once soomn Si sai 20008 ‘sass tenet eons sna seete tenis ceean’ eoone seeee: ont
{
{ om m aaaaaik k eeeee 88888 00000
£ ommmm a a k ok e 8 8 © Q
{ mmm a a kik e 8 8 QO 0
Cc om m aaaaakk eee 8 Q QO
{om m oa a kk e 8 8 Q Oo
{ om m a a k ok e 8 8 Oo 8)
{ om m a a k k eeeee 88888 O0000
{ Here begins the main program block of makes8o
{ The pracessing loop scans each line and
{ decides whether to add it to the text part
{ of the descriptians database, ar whether to
{ interpret it as a makedesc instruction line.
{ The instruction lines are handled by a case
{ statement which uses the special character
{ at the beginning of the line to determine
{ which makedesc instruction to carry out.
{ asi Sp bc ane Gi Sean pss Sasa ei ies Sst soi Sie ombs Suk cng ete Secs cds sleS day Sai em es so5e8 Shs con oa te i Se an a en sot Cebos HS ise at cess Se
REGIN
blank8O := concat (blank40, blank40);
nextplace := 0;
outrec 2= O88
fet DP eS hd BP RS ee ee ke he as hw ke
155
FOR i s= © TO hashmax
hashlid := 0
{ enddo 34
write (tracing? ===>") 5
readin (ch)s
THEN
tracing #= true
ELSE
tracing := false
{ endif 335
2
iname := "74
WHILE length (iname) =O
DO
BEGIN
write (“input file===>");
readin (iname);:
END ¢£ DO 33;
nname r= 7" 5
WHILE length (nname) = 9
DO
REGIN
write (‘description file===>")45
readin (nname) ;
END ¢ DO 33
xname := concat (nname,*.x*) 3
reset (infile, iname) 5
rewrite (narrate, nname);
Cale al ah
a)
4
main processing loop }
o a
af
156
REFEAT
readin (infile, line);
IF length (line) = ©
THEN
BEGIN
ch s= linellids;
CASE ch OF
$s
BEGIN
line := copy (line, 2, length (line) — 1)35
IF length (line) = 40
THEN
REGIN
writeln ("placename too long’);
write (*’chopping tos *)3
line := copy (line, 1, 40);
writeln (line) s;
END { IF 33
IF length (line) « 40
THEN
line :=
concat (line,
capy (blank40, 1, 40 ~ length (line)))
{ endif 33
placename := line;
entername;
END:
eae findrec:
BEGIN
157
close (infile);
iname := copy (line, 2, length (line)-1)3
reset (infile, iname) ;
END;
END € CASE ch OF 33
IF NOT (ch IN warnset)
THEN
describe (line)
cn
endif 33
note: this takes advantage of the u.c.s.d.
"fall through" semantics of the case
statement and in general will not
be transportable code. +
a
END ¢ IF length (line) ?0 3;
UNTIL eof (infile);
places€nextplacel.dend := outrec ~ 15
{ finish last placerec }
nextplace := nextplace + 1;
PlacesCnextplacel.name := *nowhere’;
Lr me ese mee tte tee te es ae tne tte mht ns ec ns tte
€ write out index file
r
SL metre eee eee eens stte see tn ae anne ane ste ey nee sete arnt eee mn ett tam aes ens ee
hw tw he
close (narrate, lock)s
rewrite (Cindexfile, xname);
dumprec.section := indexsection;
{ SET variant TO accept hash table entries }
FOR i s= 0 TO hashmax DO
BEGIN
dumprec.tableentry := hashlids;
158
seek Cindexfile, i)43
indexfile™ := dumprecs;
put (Cindexfile);
END £ DO 35s
dumprec,. section := descsectians;
{ SET variant TO accept place table entries
FOR i 2= 1 TO nextplace DO
REGIN
dumprec := placeslid;
seek (Cindexfile, i + hashmax)s
indexfile™ := dumprec;
put Cindexfile)s;
END ¢ DO 35
close (indexfile, lock);
END.
LISTING 19-2. BROWSE80 DATABASE SNOOPER
{3} Comment brackets need an opening and closing bracket
i
:
i
$
i
i
}
i
b ~ Oo w 8S @ & ©
which have been generated by makego,
eA eA eA ee ee en rl
' :
i
i
i
i
i
i
j
H
j
i
i
!
:
i
i
i
PROGRAM browse;
CONST
hashsize = BH; Use 30, not 31
blank4o =
: 40 spaces between apostrophes "4
TYFE
whichsect = (indexsection, descsectian) ;
Fragram far previewing makedesc databases
had
RS hs he hs ke hw we
159
pname
storyline
byte
procs
placerec
RECORD
li
STRINGC40];
STRINGCBOT;
Oe. ees
(pnamelookup, pentername,
pdescribe, pmakename,
pfindrec);
CASE section:whichsectiaon OF
indexsection:
descsection:
ENDs
VAR
infiles
narrates
xfiles
hashs
symhashs
where:
{ index TO symbal table
places:
dumprecs
lastrecs
starts:
stop:
nextplaces
160
( tableentry: INTEGER )3;
( name: pname;
id: INTEGER;
dbegins INTEGER;
dends INTEGER:
link: byte 5
texts
FILE OF storyline;
FILE OF placerecy}
ARRAYCO,.hashsize] OF byte;
INTEGER:
INTEGER
Hy
ARRAYCO..255] OF placerecy
placerecs;
placerec;
INTEGER;
INTEGER;
bytes £ init= 0 }
outrec:
r
placename:
line:
blank@os:
iname,
NNamMe,
xnNAamMes
is
ditto:
{ was last data
tracing:
ches
warnsets
{ init = (C’*$*,?*
{ index TO descriptians. init=0 }
INTEGER;
ba
pnames
storylines
storylines
STRING;
INTEGER:
BOOLEAN;
line a ditto line? }
BOOLEAN;
CHAR;
SET OF CHAR;
we os ys ,
a o ] e
FROCEDURE trace (whorprocs) ;
BEGIN
CASE who OF
pnamelookups
pentername:
pdescribe:
pmakename:
pfindrecs
END ¢ CASE who
writeln(*?namelookup’
writeln ( entername’ )
writeln(* describe’);
writeln (* makename’* ) 3:
writeln(* findrec”®)s;
+
a
END { PROCEDURE trace }5
PROCEDURE into (whos procs);
BEGIN
~
161
IF tracing
THEN
REGIN
write (‘entering *)5s
trace (who)s
END:
END { PROCEDURE into 33
FUNCTION namelookup ¢ thisname: pname ): BOOLEAN;
REGIN
into (pnamelookup) ;
places(CO].name := thisname;
where >= hash€symhashd;
WHILE placesCwherel.name <> thisname
DO
where := places Cwheredld. link
£{ enddo 3;
namelookup == ( where <> 0 )3
END ¢ FUNCTION namelookup 33
{ qd @ S$ ¢ r i b @ 3
3
FROCEDURE describes
BEGIN
into (pdescribe);
start := placesCwherel.dbegins
stop := places(C€wherel.dend;
FOR i s= start TO stop
162
seek (narrate, i):
get (narrate) ;
write (narrate) ;
VD ¢ DO };
END { PROCEDURE describe 3;
i
}
i
!
}
{
t
H
|
i
t
i
i
:
i
i
kes ae
!
i
i
'
H
'
i
H
i
H
Re
FROCEDURE makename (VAR thisname: pname?
BEGGIN
into (pmakename) :
IF length (thisname) «= 40
THEN
thisname :=
concat (thisname,
copy (blank40, 1, 490 —- length (thisname)))
{ endif 33
writeln (*placename: *,thisname) ;
END ¢<¢ PROCEDURE makename 3;
BEGIN
blank80O := concat (blank40, blank40O);
nextplace := 13
write (tracing? ===");
readin (ch);
IF ch="y*
THEN
tracing := true
ELSE
tracing := false
{ endif 3
nname := "75
WHILE length (nname) = 0
163
DO
REGIN
write ("description file===>");
readin (nname);
END ¢ DO Fs:
xname := concat (nname,
reset (xfile, xname);
reset (narrate, nname) ;
FOR i s= © TO hashsize DO
BEGIN
hashlCild = xfile*.tableentry;
get (xfile);
END;
ios= 13
REPEAT
placeslild := xfile“;
get (xfile);
ios= i + ds
UNTIL eof (xfile)s
close (xfile, lock);
Ls ht
Lf
\.
{ main processing loop
{
REPEAT
write (’describe what place===>");
readin (placename) ;
IF placenamef1l] = °°
THEN
BEGIN
164
7
cS
Al
~
= 0 TO hashsize
where := hashlid;
WHILE where <2? ©
DQ
REGIN
writeln (places{€wherel.name>) 4
where := placesCwherel],.link
END {€ WHILE where <> © DO 33
END < FOR i := 1 TO hashsize DO }
END
ELSE
BEGIN
makename (placename) 5
= O04
FOR i := 1 TO length (placename)
symhash :=
“ae
(symhash + ord (placenamelid)> MOD
{ enddo 3;
IF NOT namelookup (placename)
THEN
writeln ("no such place’*)
describe
{ endif 3s
UNTIL placename = *quit
END. |
36 spaces
(hashsizetl)
165
Creating a Database of Descriptions
You may have noticed in Adventure 1 how much of
the program consisted of writeln statements. Put-
ting all that creative prose straight into the program
takes up an enormous amount of space. The space
could be used for other Pascal code; code that could
make the game more complex, devious, creative,
and challenging. It would be better if most (if not
all) of the text of adventure descriptions were
stored outside the program code. The natural place
for such storage would be on the disk. The adven-
ture program could then refer to the disk whenever
it needed to describe a particular location. The
program space freed by removing descriptions can
then be used to add more problems, locations, and
challenges to the game itself.
How can this idea be put into practice? There
are some new problems to solve!
1. How to create a file containing the text of ad-
venture game descriptions.
2. How to make sure the file is structured so that
the description of any location can be quickly
retrieved.
166
3. How to write Pascal procedures in adventure
games(s) that read the descriptions from the file
created in step 1.
In this and the next few chapters, I shall at-
tempt to help you solve these problems. In this
chapter, I describe a utility program which solves
problem 1.
The utility program or “tool” is called
MAKEDESC. A listing of MAKEDESC was in-
cluded in Chapter 19. You can type it into your
system and use it. MAKEDESC generates or
creates a database of location descriptions for use in
your adventure game. What, you may ask again, is a
database? A database is any collection of informa-
tion. Your kitchen recipe file is a database. The files
in the office of any business form a database. Your
income tax records form a database. However,
when computer people use the word database to
refer to a collection of information, they usually
have another requirement in mind:
The information in a database is structured for
easy retrieval.
MAKEDESC creates a database of descriptions MAKEDESC: THE DESCRIPTIONS GENERATOR
of adventure game locations. It is made so that you MAKEDESC has the following inputs and out-
can easily retrieve the descriptions using a simple puts:
Pascal procedure. I will show you how to do that in
Chapters 21-23. In the remainder of this chapter,1 >> Input: one or more text files telling
discuss how to use MAKEDESC to create the de- MAKEDESC what the descriptions are and
scriptions database in the first place. how to divide them up into different locations.
Create textfiles containing
descriptions plus MAKEDESC
instruction lines (explained
in the text).
X(ecute the MAKEDESC (MAKE80)
program, specifying your
first textfile as input.
MAKEDESC will prompt for this
information.
Descriptions
1) Text
MAKEDESC Database
Descriptions b program (fn)
Text file processing
(or files)
J
Descriptions
Index
File
(fn.x)
Fig: 20-1. A schematic diagram of the usage of the MAKEDESC program.
167
>> Output: two files—an index and a descriptions
file.
The process of using MAKEDESC is illus-
trated schematically in Fig. 20-1. The input file to
MAKEDESC will consist of ordinary text, which
forms descriptions of locations, interspersed with
MAKEDESC instruction lines. Here is part of a
typical MAKEDESC input file:
$At the end of a dusty road
You are standing at the end of a dusty road.
In the distance | can see a brick building with
a well in front of it.
$In a maze of confusing tunnels
You are in a maze of twisting, confusing
tunnels. Everything looks alike in here. | am
having great difficulty finding my way.
$maze2
$Near a volcano
You are near an active volcano. | can hear
rumbling and | smell a sulfurous odor in the
air. Even though it is dark here, a faint red
glow shatters the darkness and suffuses the
air with light.
Any lines beginning with one of the MAKE-
DESC instruction characters shown in Fig. 20-2
will be processed by MAKEDESC as special in-
structions to itself and not as part of the descrip-
tions.
All other lines will be taken as textual descrip-
tions of a particular location. I will refer to such
lines as description lines. Description lines must be
40 characters or less in length. This restriction is
there because MAKEDESC was originally written
on an Apple II without an 80 column display card.
The text display of the Apple II is only 40 characters
wide. If you want to modify MAKEDESC to allow
longer description lines, you may. Later on, I'll
show you how.
HOW TO USE MAKEDESC INSTRUCTION LINES
There are three types of instruction lines:
168
Fig. 20-2. The MAKEDESC instruction characters.
1. Placename lines—instruction lines beginning
with a $.
2. Ditto lines—instruction lines beginning with a
3. Append file lines—instruction lines beginning
with a >.
Placename Instruction Lines
MAKEDESC requires that the description of
each location, which may be many lines long, be
immediately preceded by a placename instruction
line. Each placename instruction line tells
MAKEDESC two: things:
1. Stop collecting the description of the last loca-
tion.
2. Create anew location with the name provided in
the placename instruction line and prepare to
start collecting its description.
Suppose the input contained the instruction line:
$Pit of Darkness
At this point MAKEDESC would close off its cur-
rent description. It would create a new location
whose name would be “Pit of Darkness” and as-
sociate subsequent description lines with that loca-
tion. The description of the Pit of Darkness would
encompass all description lines in the input up to
(but not including, of course) the next placename
instruction line:
$Pit of Darkness
You are in a Pit. It is pitch black.
The darkness is so great you cannot see the
brightly burning lantern you hold at arm’s
length. The light from the lantern is quickly
absorbed by the greedy, all encompassing
blackness. You can almost feel the dark-
ness, like a sinister, oily, choking, sub-
stance. You are very close to panic.
$A long narrow tunnel
The description of the Pit of Darkness will be
taken as all the text lines between the two
placename instruction lines.
$Pit of Darkness
and
$A long narrow tunnel
There are some restrictions on the use of
placename instruction lines:
@ You may not have duplicate placenames in your
input. In the previous example, if MAKEDESC
encountered a second placename instruction line
$Pit of Darkness
it would complain, and tell you:
*«* duplicate placename: Pit of Darkness
The way MAKEDESC is currently written, it will
also mess up the old description of Pit of Darkness
in such a situation—so my advice is—Don’t do it!
@ Placenames themselves, such as “Pit of Dark-
ness” or “A long narrow tunnel” must be 40
characters or less in length. If you put in a
placename that is longer than that, for example
$The Brobdignagian gold, silver, diamond,
and platinum mines
MAKEDESC will chop off everything after the for-
tieth character and issue the message
Placename too long.
Chopping to: The Brobdignagian gold,
silver, diamond,
This will not do any harm to the subsequent
descriptions of the place. However, the placename,
if used in an adventure game program will be per-
manently “stunted.”
This restriction is more or less arbitrary.
Later we will show you how to get longer place-
names (they just take up more space).
m@ There may be no more than 255 placenames in a
given MAKEDESC database. This is a limitation
imposed by the way that the internal MAKE-
DESC data structures were designed.
DITTO LINES
Instruction lines that begin with quotation
marks (“) are known as ditto instruction lines. They
are provided mainly for convenience, and may be
used to repeat a previous description. They are to
MAKEDESC as ditto marks are to handwritten
manuscripts.
There are two ways to use “:
@ Without a placename.
gw With a placename.
If you use “ all by themselves, MAKEDESC
will repeat the description of the last placename
(this includes the possibility that the last placename
was itself described using a ditto instruction line).
For example, suppose you want to put a maze of
“twisty little passages, all alike,” as in the original
adventure, into your own adventure database.
Study the following:
$maze1
You are in a maze of little
twisty passages. They all look
alike.
169
$maze2
“c
$maze3
$maze4
“
$maze5
Notice first the ditto lines. Each of them will
cause the corresponding placename instruction to
use the description given under the place maze1.
Notice also that for each new place that uses a ditto
instruction line, a separate placename instruction
line is required. So it is useless to try saying
$maze
You are in a maze of little
twisty passages. They all look
alike.
This doesn’t work because MAKEDESC is too
dumb to create a place without aname. When it sees
a ditto instruction line, it assumes that you have
already created a new place that has been described
by a placename instruction line.
If anything follows the “ on a ditto instruction
line, MAKEDESC does something different. It will
try to interpret whatever follows the “ as the
placename of an already existing place. If it suc-
ceeds in doing so, it will use the description of that
place as the description of the place to which the
ditto applies. You really need an example to under-
stand this one!
$maze1
You are in a maze of twisty little
tunnels, all alike.
$maze2
You are at a dead end in the maze.
$maze3
“ maze
$maze4
170
“ maze2
$maze5
“ maze
In this example, you have created 5 places:
maze1, maze2, maze3, maze4 and maze5.
There are two descriptions: the first applies to
maze1, maze3, and maze5; the second applies to
maze2 and maze4.
Caution. The name (if any) used in a ditto
instruction line must be the name of an already
described place. The name following “ is not used to
create a new place. You must create the place first
by including a placename instruction line. Study the
above carefully to help clarify these points in your
mind.
CONTINUATION FILE LINES
Textual material takes up lots of space, even
on disk. On many systems, if you are writing a
really big adventure game, you won't be able to fit
all the input to MAKEDESC into a single UCSD
Pascal text file. Because I don’t want to limit your
ability to create lots of exciting descriptions, I have
given you a way to “chain” text files together when
you are using MAKEDESC. Lines beginning with
the > character are called continuation file instruc-
tion lines.
If the last line of a MAKEDESC input file is
>advent2.text
MAKEDESC will stop processing that file and con-
tinue with the file named advent2.text. Any lines
that follow a continuation file instruction line will be
ignored by MAKEDESC. That is, when it goes toa
continuation file for further input, it never returns
to the original file.
On the other hand, there is no limit to the
number of continuation files that MAKEDESC can
process in a given run. Well, that’s not quite
true—there is always the limit imposed by the total
amount of disk space available! Think of continua-
tion files as being “in line” to be processed by
MAKEDESC. This is illustrated schematically in
Fig. 20-3.
First input file
> fileb.text
Continuation
input file
fileb.text
Fig. 20-3. The effect of the use of a continuation instruction line.
RUNNING MAKEDESC
Now, how do you actually use the MAKE-
DESC program on your own computer? First, of
course, you must obtain an executable version of
MAKEDESC. To do this, perform the following
steps:
1. Enter the source code of MAKEDESC as given
in Chapter 19 into a .text file, using the UCSD
Pascal editor.
2. Compile MAKEDESC.TEXT to produce
MAKEDESC.CODE.
Details on these steps will be found in the
documentation for the UCSD Pascal system on your
particular computer.
Once you have compiled MAKEDESC. TEXT
to obtain MAKEDESC.CODE, you need to con-
struct input for MAKEDESC to “chew” on. This
will be constructed, again using the UCSD Pascal
editor. It will consist of a mixture of place descrip-
tions and MAKEDESC instruction lines, as de-
scribed in the first part of this chapter. You will
create one or more .text files containing this input.
If you have more than one file, make sure to include
the appropriate continuation file instruction lines
needed for MAKEDESC to link them together.
To execute MAKEDESC, simply type X in
answer to the UCSD system level prompt. Then
when the system asks:
Execute what file?
respond with
MAKEDESC
One word of caution is in order here. If you
have more than one disk drive attached to your
system, you may have to answer the prompt dif-
ferently. For example, if you have two drives, and
MAKEDESC is on the “nonboot” drive, you may
have to type:
171
See “A note about disk Drive
numbers in file names and
commands” on page 3 Of creates. There is one slight restriction here: the
#5:MAKEDESC
this PDF.
What you type will depend on whether or not you
have entered the Filer program and issued a prefix
command. For details on the concepts of volumes,
file names, prefixes, and so on, consult your “local”
UCSD manual.
After you succeed in answering the prompt
correctly, MAKEDESC will be loaded and it in turn
will prompt you as follows:
Program looks only for
lowercase “y”, all other
input is treated as “no”
This prompt asks whether or not you would like to
see a dynamic trace of all the procedure names in
the program as they are invoked. It was built into
MAKEDESC as a debugging aid. Unless you are
curious and insist on seeing this information, you
may safely “answer” this prompt by pressing the
RETURN key.
The next prompt will be:
TRACING?===
INPUT FILE===>
Here you should respond with the file name of your
first input .text file; for example
*
INPUT FILE===> #[5:ADVENT1.TEXT
Notice that a volume number may be necessary, as
before.
Next you will see
DESCRIPTION FILE===> **
This is the last prompt from MAKEDESC and the
name you type in response will be used by
MAKEDESC as the name of the output file that it
* The square bracket above is probably a typo.
description file name may be at most 13 characters
in length. The reason for this is as follows.
MAKEDESC actually creates two output files: the
descriptions data file and the descriptions index
file.
The descriptions index file is used by your
adventure game to quickly locate the part of the
descriptions data file containing a given descrip-
tion. How all this works will be explained in sub-
sequent chapters. For now, however, all you need
to know is that the descriptions index file name is
obtained from the descriptions data file name by
appending the string .x (the X is for indeX). All file
names in the UCSD system must be at most 15
characters in length. Therefore, the descriptions
data file name, which by definition is two characters
shorter in length than the descriptions index file
name, must be at most 13 characters in length.
After you answer the prompts, MAKEDESC
will chug away building your descriptions database.
As each new placename instruction line is encoun-
tered by MAKEDESC, it will echo the placename
to the terminal screen:
placename: Above ground
placename: In a pit
placename: Up a tree
and so on. This will give you feedback on the pro-
gress of MAKEDESC. It will also let you know
where MAKEDESC was should anything go seri-
ously wrong during processing.
When MAKEDESC finishes processing, it will
have built two files, as described earlier. These
files may then be used in your own adventure game
programs. How this is done will be described later.
** Just type the database name with no extension. For example “advent1” instead of “advent1.text”
(in this example MakeDesc will output two files: “advent1” and “advent1.x”)
172
Random Access Files in UCSD Pascal
The program MAKEDESC described in Chapter 20
produces a database of adventure game descrip-
tions. This database is stored in two UCSD Pascal
files: an index file and a data file. The index file is a
sequential file, but the data file is a random file.
This chapter will explain the concept of random file
and discuss its implementation in the UCSD Pascal
language.
WHAT ARE RANDOM AND SEQUENTAL FILES?
All files contain records. A file all by itself is
not sequential or random, although people some-
times use the term random file. It is the access to the
records in a file that may be either sequential or
random. So when I say “sequential file,” I mean
“file accessed sequentially.” When I say “random
file,” I mean “file accessed randomly.”
Sequential Access
When you are using sequential access to a file,
the records must be retrieved in the order in which
they are stored. Access usually begins with the first
record in the file. Therefore, in order to access the
last record in a file, using sequential access, you
must first access all the records in the file that come
before it. A file that is accessed sequentially is like
aroll of candy or breath mints as shown in Fig. 21-1:
in order to get to the last candy, you must first
remove all the ones before it.
This is fine and dandy—when it comes to
candy! Database files are a different story. Con-
sider an adventure game descriptions database.
Suppose you accessed the place descriptions se-
quentially. To reach the description of the last
place, you would have to read through (but not
display) all the place descriptions preceding the last
one. In general, you would have to read through any
descriptions preceding the one you wished to show
to the player. You would be sitting around waiting
most of the time! You say you don’t believe that —
let’s do some calculations.
Suppose your adventure game has 100 loca-
tions and the descriptions of these locations aver-
age five lines apiece. The file of these descriptions
would contain approximately 500 lines, each being
173
Fig. 21-1. An example of sequential access in real life.
readable as a Pascal string variable. Then, on the
average, you would have to read 250 strings in
order to reach the specific one that begins a par-
ticular description. Now I hear you saying to your-
self: “Wait! Computers are fast. They can read
those strings in no time at all.” But consider that a
typical minifloppy disk access takes 50 milli-
seconds—that is 50/1000 seconds or one-twentieth
ofasecond. Then in order to read 250 strings, it will
take 250 x 1/20=12.5 seconds. This means that on
the average, you will wait twelve seconds before
you see the description of the next place. Most
players will probably want responses to be dis-
played more quickly than this. You can try this
experiment on your own system to see how far off
my estimates are. Here are two short programs to
use. The first creates a file of 500 strings. The
second reads through the file sequentially until it
gets to the 250th string in the file. Try them out and
use your stopwatch!
THE FILE CREATING PROGRAM
FROGRAM wirtxts
VAR
recs STRING(SO)];
fz FILE OF STRINGLS8OI;
is: INTEGER;
BEGIN
rec := Press Return after the equal sign
"You are in mountains. Tea
see all the snow-capped
monsters hereabouts,’s
rewrite (f, “junk.text*) 3
FOR 2 s= 1 TO SOO DO
REGIN
fC" f= recs
put (fds
END;
close (f, lock);
END.
THE FILE READING PROGRAM
FROGRAM rdtxts
VAR
rec: STRINGCS8OI;
f: FILE OF STRING(S&O7;
i: INTEGER;
BEGIN
reset (f, “junk.text*):
writeln (* Commencing
reading.«.")%
FOR i = 1 TO 250 DO
BEGIN
f° f= recy
get (f)5
END;
writeln ("Got it"):
writeln (f%)5
close (f, lock);
END.
Type as
one line
followed
by Return
In both of these programs as written, ‘junk.text’ is written to and read from Drive 1 (Volume #4:)
174
See “A note about disk Drive numbers in file names and commands” on page 3 of this PDF.
RANDOM ACCESS
Random access to a file means that any par-
ticular record in the file may be retrieved directly.
No other records must be read first. A randomly
accessed file is like the same roll of candy—but
with the wrapper removed! You can grab any of the
candies you like at any time.
There is a price to be paid for the ability to
access a file randomly:
All records in a random access file must be of
the same fixed size in order for random access
to be practical.
In order to understand this limitation, you
must delve into the workings of random access
files. Here is roughly how they “work.”
Each record in the file is a certain “distance”
from the beginning of the file. This distance is easy
to calculate from two pieces of information:
1. The size of the records in the file (which I have
assumed is the same for all records in the file).
2. The number of the record sought. Records are
assigned numbers, beginning with the number
0, according to their position in the file.
Once the position of a given record is calcu-
lated, the computer hardware can be given instruc-
tions to seek to that position in the file. This means
that the disk drive’s read mechanism is moved in
such a way that the record in question will pass
under the read head. Then, simply reading a record
from the file will produce the desired result. It is
almost as if each record were its own tiny, separate
file. Of course, all this depends on the ability to
calculate the position of the record.
Limitations of Random Access
The concept of a random access file is not part
of Standard Pascal. It was added to the UCSD Pas-
cal language and must be considered an extension.
Actually, that is not quite true—you may declare
files of fixed size records in Standard Pascal. In fact,
because every file consists of records of some pre-
declared Pascal type, they are automatically of
fixed size. (The exception to this is the text file:
FILE OF CHAR. A “record” may be thought of as a
line of text that is terminated by an anonymous
internal EOL.)
UCSD Pascal and Random Access
The UCSD Pascal language has added support
for accessing files randomly. This support is sur-
prisingly simple. It consists of a single new intrinsic
procedure called seek:
seek (fileid, recnumber);
This procedure can be called to p»sition the file to
the correct record, according to the value contained
in recnumber. Following a call to the seek proce-
dure, a call to get or put may be made, just as if the
file were being read or written sequentially.
There are some rules to remember when using
seek:
@ Don’t do two seeks in a row without an interven-
ing get or put.
@ Remember that the get or put that follows a seek
moves the file window. This is just the same as if
no seek had first been performed.
gw EOF (f) is always false after a seek has been
performed.
You may ignore all this if you don’t plan to use
seek in your own programs. The use of seek in the
adventure games in this book is always the same.
The code is already written and works—you may
simply imitate it and you won’t go wrong.
THE BROWSE PROGRAM
In Chapter 19, I presented the listings of two
Pascal programs. One is the MAKEDESC adven-
ture database generator program. The use of
MAKEDESC was described in Chapter 20. The
other is BROWSE, a program that may be used to
review the output of MAKEDESC before including
that output in one of your adventures.
BROWSE uses all the same declarations as
MAKEDESC. It starts out by prompting you for the
file name of your descriptions. This is read by
175
BROWSE and used to open the descriptions file for
reading. The name of the description file is used to
derive the name of the index file, as described in
Chapter 20. The index file is also opened for read-
ing. BROWSE uses the index file and the descrip-
tions file in the same way that adventure games do.
The BROWSE program prompts the user for a
placename. You may respond with one of the
placenames used in generating the descriptions
database. In this case, BROWSE will look up the
place by name and print its description on your
terminal. If you don’t have a list of the placenames
handy, you may also type ? in response to the
prompt for a placename. In this case, BROWSE will
print a list of all the placenames in the database you
are reviewing.
USING DESCRIPTIONS
DATABASES IN YOUR ADVENTURES
If you read Adventure 2 carefully, you will see
176
how to incorporate the databases generated by
MAKEDESC into your own adventures. There are
several key points to remember:
w Declare the relevant types and variables. These
are discussed in more detail in the next chapter.
m@ Make sure to open the descriptions data and
index files in the initialization procedure in your
adventure.
m@ Incorporate the show procedure, used to re-
trieve and display each description from the
database. The show procedure is almost identi-
cal to describe procedure in the BROWSE pro-
gram. More detail on how describe and show
operate will be given in the next chapters, which
discuss the details of the structure and pro-
gramming techniques associated with the de-
scriptions database generated by MAKEDESC.
The Structure of Adventure Databases
In Chapter 21 I briefly described the files compris-
ing an adventure database. In this chapter I shall
delve into the detailed makeup of those files. In the
process, I shall elaborate on several topics:
@ Index files in general and the descriptions index
in particular.
@ The use of Pascal variant records in building the
index file.
@ The incorporation of the database in Adventure
2—the use of the show procedure for retrieving
the description of a place and so on.
m@The BROWSE program for previewing your
database after creating them.
INDEX FILES AND THE DESCRIPTIONS INDEX
An index file is analogous to an index in a book.
It contains information about where in the data
file(s) of a database specific entries may be found. In
the adventure database, the data consists of de-
scriptions of places or locations, in the adventure.
The index file consists of entries, each of which has
information about a specific location.
The adventure database index file actually has
two sections, as shown in Fig. 22-1. The first sec-
tion is an array of numbers known as hash values.
The techniques of hashing and hash table lookup are
discussed in the next chapter. They are mainly of
interest to the MAKEDESC program, which
creates the database, and the BROWSE program,
which may be used to preview the database. Ad-
venture 2 does not use the hash table part of the
index itself. The use of the hash table is a key to
being able to retrieve the description of a location
given its name. This information is quite vital to the
operation of MAKEDESC in particular, because
MAKEDESC checks for the presence of duplicate
placenames.
The second part of the index file is a collection
of records that I shall call description information
entries. Each of the records contains the information
needed to locate the description of one particular
adventure location. The Pascal type declaration
that isin MAKEDESC, BROWSE, and Adventure 2
reveals the nature of the information contained in
these records.
177
section
placerec =
RECORD
CASE section: whichsection OF
hashsection: (tableentry: INTEGER);
descsection: ( name: pname;
id: INTEGER;
dbegin: INTEGER;
dend: INTEGER;
link: byte );
END;
This declaration is an example of what is known in
Pascal as a variant record type. It enables the infor-
mation stored in variables of that type to be
nonhomogeneous. The case statement in the rec-
ord declaration introduces the variants. Each case
label in the declaration represents one of the possi-
ble variations that the record can take on. In this
example, the record type placerec has exactly two
variants—hash section and descsection. The first
variant accounts for the records in the hash-table
178
Fig. 22-1. The format of the index file of a
descriptions database.
section of the index file. The second variant ac-
counts for the remainder of the file, which consists
of one record for each description in the data portion
of the database.
All three programs, MAKEDESC, BROWSE,
and Adventure 2, use the placerec data type in the
declaration of a file:
VAR
xfile: FILE OF placerec;
The file window xfile ~ always contains a record
from xfile. The structure of that record may match
the hash section record or the descsection record.
This depends on which variant of the record type
has been selected by the program. The section
variable declared in the case statement of the rec-
ord type declaration is used to control and re-
member this at run time. I will touch on this again in
Chapter 23 when I discuss the operation of the
BROWSE program.
Contents of the Descriptions Index Entries
The descsection record structure shows sev-
eral fields in the corresponding records. Each of
these fields is present in a descriptions index entry
for a location. The fields are described as follows:
@ name
This is a string containing the name of the loca-
tion as it was present in the “source” file that
MAKEDESC used to process this entry when the
database was generated. The user-defined type
pname is used to limit the size of the string in
order to conserve file space. The value 20 was
chosen as the maximum length for values of this
string. The names of locations could be used as
shorter descriptions, in which case it might be
desirable to allow for a slightly longer string.
wid
This is an integer that records the positional
value of the individual location in the list of loca-
tions. It is a redundant piece of information that
was used in debugging MAKEDESC originally
and was never removed from the declaration of
placerec. It “wastes” two bytes of disk space per
index entry. If you feel that this is an intolerable
misuse of disk space, you can easily remedy the
situation. Simply remove the id: field from all
declarations of the placerec data type.
@ dbegin
This is an integer that gives a record number in
the data file of descriptions. It is the first record
number for the description of the location to
which a given index entry corresponds. It tells
the show procedure or other retrieval code how
to get to the right place on the disk in order to
start reading the location’s description.
@ dend
This is an integer that gives another record
number in the data file of descriptions. It is the
record number of the last line in the description
of the corresponding location. It tells the show
procedure when to stop reading description lines
from the data file.
*See “A note about disk Drive numbers in file names and commands” on page 3 of this PDF.
@ link
This is a value between 0 and 255 that is used in
the hash table lookup algorithm. It determines
another index entry to examine should this one
not correspond to the specific name being
sought. The details of how this works will be
revealed in the next chapter.
The structure of the descriptions database
files and their relationships are in some detail
shown in Fig. 22-2.
USING THE DESCRIPTIONS
DATABASES IN ADVENTURE GAMES
Once a descriptions database for a given ad-
venture has been generated using MAKEDESC,
there still remains the question of how to access
that database in an adventure game. Adventure 2
presents a model for accomplishing this.
Initialization of the Database
There are two files that need to be made ac-
cessible to the adventure game:
FILE OF placerec;
FILE OF storyline;
xfile:
narrate:
The narrate file is the descriptions data file. It only
needs to be opened in order to be accessible for
reading later on. This is accomplished in the ini-
tialization procedure in Adventure 2 via the state-
ment:
reset (narrate, “a2.data’); *
The xfile file is the index file and has been discussed
in detail above. It must be read into memory by the
adventure game in order to be used for retrieval
purposes. The xfile file is opened by the statement:
reset (xfile, “a2.data.x’’); *
Then the following code reads in the index file and
stores the descsection records in the array called
places:
179
Indexfile: A2.DB.X
Datafile: A2.DB
All the text
for the
description of
this location
FOR i := dbegin TO dend DO
BEGIN
seek (narrate, i)
get
(narrate);
write (narrate™)s;
ENDs
The seek command in this code accounts for the
fact that none of the has‘section records stored in
the index file are actually used by the adventure
game program. The records numbered 0 to 31 in the
index file are all hash section records. Therefore, in
* hash, not has
180
Fig. 22-2. The file structure of the entire
descriptions database.
The two sections of code
shown on pages 180 and 181
are swapped.
order to skip over them, the initialization code first
“seeks” to position 32 in the index file. After that,
each call to the I/O get procedure will advance the
record pointer one position in the index file. That is
why no further calls to seek are necessary inside
the repeat loop.
The Operation of the show Procedure
Once the index file has been read into the
places array, the procedure called show is used to
retrieve various descriptions and show them to the
player. The key to the operation of show is the
following loop:
seek
(xfile,
2) 8
get (xfile)s
places Cloc] := xfile™s;
REFEAT
(loc);
loc 8 succ
get (xfile);
r= xfile‘;
placesfloc]
UNTIL loc =
flames;
The interpretation of this is simple, given what you
know about the descsection records. The show
procedure starts at dbegin for a given location and
goes to dend. For each value of i between those two
numbers, there is a line of description in narrate.
The show procedure calls seek to position itself to
the correct place in the narrate file; then it calls get
to retrieve the line of description, and finally it calls
write to display the line on the screen.
This for loop uses dbegin and dend directly.
Actually these are abbreviations for:
places[where].dbegin, and
places[where].dend.
The with statement that surrounds the for loop is a
Pascal method for abbreviating references to com-
ponents of records. If you say
WITH places[where]
you can refer to any of the fields of the record
represented by places[where] without repeating
the prefix places[where]. This is not only a way of
saving the programmer some typing. It also allows
the Pascal compiler to generate better code for
accessing the corresponding record fields.
THE BROWSE PROGRAM
FOR PREVIEWING DATABASES
In Chapter 19 I presented the listing of
BROWSE. BROWSE is a simple program, similar
to MAKEDESC, which you can use to examine
adventure game databases that you have generated
with MAKEDESC. BROWSE allows you to ask for
locations by placename, then it displays the corre-
sponding description from the data file generated by
MAKEDESC. It has a convenience feature for
those situations when you can’t remember the exact
names of the locations in your adventure database.
If you type ? as the name of a place to be described,
the BROWSE program will display the names of all
the locations in the database you are examining.
181
Programming
Techniques Used in MAKEDESC
In this chapter I delve into the programming
techniques used in MAKEDESC. I discuss the fol-
lowing ideas:
@ Symbol tables (information gathering and stor-
age).
@ Hashing (hash tables, hash values, and so on).
m@ Symbol table lookup using both linear search
techniques and hashing.
These ideas are taken from computer science. They
may be a bit technical for some readers. However,
you don’t need to understand them in order to use
the MAKEDESC program itself. If all you wish to
do is use descriptions databases in your own ad-
ventures, you can probably skip this chapter en-
tirely. On the other hand, if you make your way
through this material, you will have some valuable
programming techniques that will serve you in
many other situations as well.
SYMBOL TABLES
The term symbol table is usually encountered in
discussions of programs like assemblers and com-
182
pilers. Symbol tables keep track of information
about source programs that compilers or as-
semblers process. The UCSD Pascal compiler has a
symbol table that, among other things, keeps track
of the identifiers declared in a Pascal program.
MAKEDESC is a much simpler program than a
compiler or assembler, but it nonetheless uses a
symbol table. The symbol table in MAKEDESC
keeps track of placenames and information as-
sociated with placenames that is needed for build-
ing the descriptions database.
The information kept in the MAKEDESC
symbol table has already been described in the
previous chapter. In fact, a large part of the
MAKEDESC symbol table is a copy of the database
index file. The additional component of the
MAKEDESC symbol table is an auxiliary array
called the hash table. I shall discuss the concept and
usage of hash tables in detail below.
The MAKEDESC Symbol Table:
Functional Requirements
In order to understand how the symbol table
works, it is useful to know what operations it needs
to perform. The operations required can dictate the
programming techniques utilized. In MAKEDESC
the following tasks must be performed:
@ The addition of a placename to the symbol table.
@ The addition of information about a placename to
the entry for that name; for example, when the
description of the placename is completely pro-
cessed, the dend value is added to its entry.
(Recall that dend represents the record number
of the last line of description of the placename. )
m The looking up of a placename in the symbol table.
This action occurs more than once. It occurs first
when the name is placed in the table to begin
with. At that time, the lookup operation fails. It
occurs again if the same placename is used later.
Then the lookup operation will succeed, and
MAKEDESC will warn the user that a duplicate
placename has been detected. The lookup opera-
tion is also part of the add-a-placename-to-the-
symbol-table operation. The lookup is performed
in order to tell MAKEDESC where to put the new
placename entry.
LOOKUP TECHNIQUES: THE LINEAR SEARCH
I have already discussed the technique of
linear searching and the variation on that technique
that uses a sentinel to simplify the search. The
linear search has its advantages and its disadvan-
tages.
Advantages of Linear Searches
Linear searching is a very simple technique. It
is easy to understand and easy to implement. Most
programmers have used this technique in one fash-
ion or another. The code for a linear search is quite
simple and easy to remember.
Disadvantages of Linear Searches
Linear searching is the most inefficient of all
possible search techniques. That is its main disad-
vantage. If there are N items that must be searched
through, on the average it will take N/2 attempts to
locate what is being searched for. If the item being
searched for is not present (which is the case most
of the time in MAKEDESC), all N items in the table
have to be examined in order to determine that the
search has failed.
As the number of items being searched
through increases, the disadvantages of linear
searching begin to outweigh its advantages. In the
case of MAKEDESC, as the number of placenames
in the adventure database increase, the inefficiency
of searching them linearly increases proportionally.
Because the number of placenames allowed by
MAKEDESC is limited to 255, this would never be
an intolerable burden. However, it was decided
when MAKEDESC was implemented to use a more
efficient search technique.
HASHING: A MORE EFFICIENT SEARCH TECHNIQUE
What is needed is a way to narrow the search
through the symbol table. If you could put a limit on
the number of entries that need to be considered
during each lookup, this would increase the effi-
ciency of the lookup process. The technique of
hashing provides just such a method. The hashing
technique uses an auxiliary array called the hash
table. The hash table allows the placenames to be
divided into anumber of smaller groups. For a given
placename, only the placenames in one of the
groups need be searched rather than all the
placenames present in the table. The smaller
groups are organized into what are called linked
lists. Each linked list consists of only a fraction of all
placenames. Each linked list is searched using a
linear search with a sentinel. The linear search of
the much smaller list of placenames is acceptably
efficient compared to a linear search of all the
placenames. In order to understand the hashing
technique, you need to understand several concepts
and techniques:
@ The idea of a hash value.
@ The computation of hash values.
@ The use of hash values to determine a smaller
list of placenames.
@ The organization of the small lists using links.
I shall discuss these ideas in the remainder of this
section.
183
The Idea of a Hash Value
A hash value is a number. It is anumber that is
associated with a string of characters such as an
identifier in a Pascal program or a placename in the
source for a MAKEDESC database. Such numbers
must satisfy some simple requirements:
1. In a given application, the hash values are lim-
ited to a certain set of values.
2. For simplicity, the smallest hash value is usually
taken to be 0. This is because hash values are
used as index values for the array known as the
hash table.
3. The largest hash value is usually taken to be a
prime number. This usually means that all the
identifiers under consideration will be divided
into relatively equal sized lists. This is clearly
desirable, because the smaller lists are being
searched using linear search. It would be self-
defeating if one of the lists was quite large and all
the others only one or two elements long.
The Computation of Hash Values
Given a character string, such as a MAKE-
DESC placename, the hash value of that string must
be computed. The technique used in this computa-
tion is usually referred to as a hash function. The
choice of a good hash function is an entire technical
subject in itself and much has been written about it.
I shall not dwell on details of that choice, but merely
present one common solution. In MAKEDESC
hash values are computed using the ORD and MOD
functions from Pascal. The method is as follows:
To form the hash value of placename s, con-
sider the individual characters s[i] of s. Each
such character has an internal number that may
be computed in Pascal by applying the ORD
function:
internal value of s[i] = ORD (s[i])
If you added all those values (for each charac-
ter in S, that is for i = 1 to LENGTH (s)), you
would obtain a number, hv. The possible val-
184
ues for that number would be quite large,
however. You want the hash value of s to range
from 0 to some fairly small number. In
MAKEDESC, the upper limit for hash values
is 30. In order to produce such a value from the
sum, hv, just described, you would use the
Pascal MOD function. The MOD function pro-
duces the remainder of one number when it is
divided by another number. The two numbers
in question here are the sum described above
and the number 31 (a prime number, as men-
tioned above). Thus, the final algorithm is as
follows:
symhash := 0;
FOR i := 1 TO length (placename)
DO
symhash := (symhash + ORD
(placename[i])) MOD (hashmax + 1)
{ END DO };
The Use of Hash Values to
Determine a Smaller List of Placenames
Once the hash value of a given placename has
been computed, how is it used? The hash value is
used as an index into the array known as the hash
table. Each entry in the hash table determines the
beginning of one of the smaller lists of placenames.
The characteristics of that list are as follows:
w@ Every placename on the list has the same hash
value.
@ The list is arranged in reverse order, that is, the
first name on the list was the last name added to
the list.
The numbers stored in the hash table array are also
index values but they index into the places array.
The places array is the array that is written out to
the descriptions index file. The hashing technique
now allows you to think that the descriptions index
file consists of a number of short lists. Each hash
table entry “points” to the beginning of one of these
lists. This is illustrated in Fig. 23-1.
hash [i]
Fig. 23-1. Hash table entries.
The Organization of the Small Lists Using Links
Each entry in the places array is a record con-
taining information about one placename. The last
item in each record is called the link field. This is
the index (in the places array) of the next placename
that has the same hash value. This piece of infor-
mation is called a link, because it determines the
next item on a linked list of items. That item is
located at some possibly remote place in the places
array, rather than being the next sequential entry in
that array. The link field tells the lookup process
how to proceed when it fails to find a given
placename at the current entry.
Figure 23-2 shows the concept of the link field
pictorially. Within the large array, places, a given
places [j]
placename entry “points” to another by means of its
link field.
To summarize the concepts of hashing, let us
consider an analogy. Think of the world headquar-
ters of a large corporation. People within this cor-
poration work in certain locations in that building. If
you set out to find a certain employee by searching
through every office in the building in order, you
might be occupied for quite a long time. On the
other hand, if you started with the knowledge of
what department the employee you sought worked
in, your search would be considerably narrowed.
The linked lists of elements all with the same
hash value may be thought of as analogous to a
department in a large corporation. They all have
185
hash [i]
places [j]
Last link:
All hash chains terminate
at places [0] which acts
as a common sentinel.
Fig. 23-2. The linked lists or “hash chains” in the places array.
something in common: in the corporation it is their
department number; in the hash search technique it
is their hash value. Searching a linked list of ele-
ments is like searching through just the offices
within a given department. There are far fewer
offices in a single department than in the entire
corporation. In the same manner, there are far
fewer elements on a single hash list than there are
in the entire symbol table (or in our case the entire
places array).
186
THE HASH TABLE LOOKUP TECHNIQUE
I conclude this chapter by reviewing the actual
algorithm used in the hash table lookup technique.
That is, once a hash value has been determined,
how is the linked list of items with that hash value
searched?
In MAKEDESC, I have used the entry
places[O] as a sentinel to mark the end of every
linked list in the hashing scheme. The hash table
itself is set up to contain all zeros to begin with.
This means that all the linked lists are empty. This
is the situation before any placename descriptions
have been read from the MAKEDESC source file.
This is illustrated in Fig. 23-3.
Adding Placenames to the MAKEDESC Symbol Table
As MAKEDESC reads placenames from its
source file (by reading placename instruction
lines—see Chapter 20), it enters them into its
hashed symbol table. The process of entering the
next name is:as follows:
@ First the hash value of the new placename is
computed.
@ The hash value of the placename is used as an
index into the hash array. The number stored
there determines the index of an entry in the
places array. If the number found is 0, there are
no placenames yet with the given hash value, and
an entry for the placename is created. How the
new entry is created and added to the table is
described below.
g@ If the number found in hash is not 0, there are
already placenames in the symbol table with the
same hash value as the current placename. The
linked list of those hash values must be searched
to eliminate the possibility that the current
placename is a duplicate. Here is the process
used:
1. Store the current placename in the name
field of places[0]. This is the sentinel con-
cept in operation: the record in places[0] is
at the end of all the linked lists in the sym-
bol table. The lookup process will eventu-
ally reach the entry if the placename under
consideration is not found earlier. The
statement
places[0].name = thisname;
accomplishes this. The statement
where := hash[symhash];
takes the value out of the hash table to
places
Fig. 23-3. The initial configuration of the hash table: all hash chains consist of only the sentinel location (places[0]).
187
places
nextplace
Situation before entry of
new place in places table:
symhash = 2
hash [symhash]= 4
nextplace= 17
hash [symhash] :=
nextplace;
=>
Adjusting the
hash chain
Fig. 23-4. A diagram of the process of making an entry in the places array.
188
determine the head of the linked list to be
searched.
2. Use the following Pascal while loop to
search the linked list of places with the
same hash value as the current placename:
WHILE places[where].name < >
thisname
DO
where := places[where].link
{ END DO };
At the end of this loop, the variable where deter-
mines whether or not the search has succeeded. If
where contains the value 0, the search proceeded
all the way to the sentinel and the lookup failed.
Otherwise, the search succeeded and where con-
tains the index of the entry that duplicated this-
name.
Adding an Entry to the Places Table
If a given placename is determined not to exist
in the places array, it is added by MAKEDESC.
The new entry must be placed into the linked list of
names with the same hash value. The variable
nextplace always contains the index of the next
available spot in the places array. This variable is
initially set to 0. It is then increased every time a
new placename is added to the symbol table. In
particular, it is set to 1 before the very first
placename is added to the table.
The process of adding the new place to the
symbol table is accomplished by the entername
procedure in MAKEDESC. Figure 23-4 illustrates
the process pictorially.
189
What Else Can You Put on Disk?
In the preceding four chapters I have discussed the
MAKEDESC program used for creating a database
of place descriptions on disk. In this brief postscript
I speculate on what else you might want to or be
able to put into disk files.
OTHER DESCRIPTIONS
The descriptions in the databases have so far
been limited to those of adventure game locations.
These are by far the most extensive kind of textual
description that has been used in either of the sam-
ple adventures so far. However, there are many
other kinds of descriptions that are not descriptions
of rooms or locations:
@ Descriptions of events that occur during the
game. For example, in Adventure 2 the ogre’s
being awakened is accompanied by some de-
scription.
@ Descriptions of speech that can be either that of
the guide in certain situations or that of other
beings that might be encountered during the play
of the game.
190
m Descriptions associated with objects that occur in
the game. There might be extra information to
convey about an object either by itself or under
special circumstances.
w@ Descriptions of places that are only given condi-
tionally. The descriptions database as described
so far is set up so that any time a location is
visited, the description is the same. Examples of
conditional descriptions in Adventure 2 are the
various descriptions given as the player digs for
the treasure.
Some of the categories of descriptions in the
above list can easily be accommodated within the
current implementation of MAKEDESC and its as-
sociated databases. I give examples in Adventure 3,
which begins in Chapter 25. Other categories are
not so easy to accommodate. One of the more dif-
ficult is the conditional description idea as it relates
to the current method of traveling. Consider the
example of the ladder room in Adventure 2, where
the response to “up” depends on whether or not the
player is carrying the treasure. In order to accom-
modate that conditionality, some additional argu-
ments to travel might be needed, tied in somehow
with the database. I leave it to you to speculate on
this further.
PUTTING THE WHOLE PROGRAM
ON DISK: ADVENTURE INTERPRETERS
A radical approach to writing adventure games
is to first design an adventure machine. This would
be an abstract machine whose only purpose was to
run adventure games. The machine language of
such a machine would be designed specifically for
executing adventure games. Programs for the
machine would be referred to as adventure code.
A Pascal program, which emulated such an
adventure machine could be written. The program
could be designed in such a way that the entire
adventure code program that it interpreted would
“live” in various disk files. This would have the
following consequences:
@ The size of an adventure would be limited only by
the size of file(s) that could be fit onto a floppy
disk. In fact, given the ability to swap disks in and
out at the behest of the emulator, there would be
no limit. That would mean you could write very
long adventures.
m The play of the game might be slower. The
adventure emulator would have to do more disk
i/o and that would slow down response time.
This might turn out to be tolerable or it might
not, depending on the details of the implementa-
tion of the adventure emulator.
@ In addition to an adventure emulator, you would
need another program that translated adventure
games in some high-level form into adventure
code. This would be an adventure compiler, if you
will. The adventure compiler would process the
specification of an adventure game in the adven-
ture language. You would have to design the ad-
venture language, making sure to include all the
necessary features to support reasonable and in-
teresting adventures.
While all of this is quite interesting and fun to
speculate on, I will not have room to expand upon it
in this book. Others before me have already acted
upon these ideas: the Scott Adams Adventures
were implemented in somewhat this fashion; Zork
and other adventures from Infocom, Ltd. were im-
plemented using the adventure language concept.
191
Preview of Adventure 3
Adventure 3 is the most ambitious adventure game
in the book. Compared to the original adventure, it
would still be rated as intermediate. However,
compared to Adventure 1 it is quite advanced.
There are more locations, more problems, better
descriptions, and more challenges than there were
in the previous examples. Much of the code is
shared with previous adventures. The newest con-
cept is the organization of the program into “units.”
OUTLINES, DIAGRAMS, AND MAPS
Because Adventure 3 is more complex than
previous adventures, there are more figures as-
sociated with it. Figures 25-1 through 25-6 present
the code outlines of Adventure 3. Each UCSD unit
used by the program is outlined, as well as the main
program, whichuses the units. Figure 25-7 showsthe
units of Adventure 3 and the uses relations between
them.
The map for Adventure 3 is large. I have di-
vided it into several parts so that it will fit into the
book’s page size. Figure 25-8 shows that the map of
192
Adventure 3 has six separate pages and also shows
the offpage connections between them. Figures
25-9 through 25-14 provide the map of Adventure 3
and Figure 25-15 presents a summary of the prob-
lems of Adventure 3.
CHAPTER PREVIEWS
Adventure 3 is described in Chapters 27
through 29. The listing of Adventure 3 is in Chapter
26. The three chapters do not repeat territory
covered in the descriptions of earlier adventures.
So if you find code that you think should be
explained but isn’t, refer back to preceding sec-
tions, and perhaps you will find it discussed there.
Chapter 27 is entitled “Larger Programs: '
Using UCSD Units” and deals with the organiza-
tional technique of program units. The UCSD ver-
sion of Pascal provides a method of structuring
called units. A unit is a self-contained part of a
program containing two parts, an interface and an
implementation. The interface of a unit contains all
“yS!] SUN 94}
“BYEPAPE JINN 40 auUlNO a4} °E aINUSAPY JO} BUI]JNO BpOd ay, ‘Z-Sz ‘B14 = Bulpnjou! auI|yNO WesBoid ay} :¢ aunjUsAPY 40} aUI]INO apod ay, *|-Sz “B14
“ON3 $-Sz ainbi4 88g $90]
9-z ainbi4 aes Lspwig
“yun eyepApe au} ul paejoep ‘Tul ¢-S2 aunBi4 88g ZSpwg
SO|QEWEA 94} JO} BpOO uOHeZI|eNIU] { +L Huy b-Sz ainbi4 aes espwg
€-Sz¢ ainBi4 8ag sqolg
NI93a 2-Sz ainBi4 eeg = Bye pApy
‘Zul IUNSIIONd
; (JUdA
‘yyul
Luu! 3YNd3I0Nd -peyw UN aas) WOGNYY uolouny e
NOLLWINAW3 Tawi apiaoid JSNW :AYWHEIT'WALSAS Woy umsaiddy 3s!) swup
00} ainpacoid,, ay} plore 0} Japs0
{
ul pasn sainpasoid uolyezieN!u|
‘€ BINJUBAPY Ul S}JUdAB aqissod Huljussaidai SajqeeA UBB|OOg
auop ‘payiy ‘uayea ‘uados! ‘eppemples ‘SaAl|i}
aweulgo
c SINJUSAPY Ul SY “paysia
‘alaysyeym
‘aueb ay} Bulunp pasn saipooB jo sjas snowea
YSE}s ‘SOyeJWWM ‘SEYWWM ‘S}JUEMUWUM
‘yeadde Aew Sjj01} 94} YOIYM JE SWUOOI $0 JOS - $904]01}
Z pue | SainjuaApy ul Se - UON}e90)
‘juaweye}s
aseo ajbuis e AjjeoiBo) si yeyM yNO Aueo 0} (Z} pue 4})
$aJNP8901d OM) SOXOAU! }! ‘SY9O|Q BPD GSON 0} Ww] azis
e JO asnedag ‘SuO!ed0] UdaMjaq |aARJ) Sa|PUeY a1ay BpoD
‘wesGoud ulew ay} ul pasejoap
UWA suoljouny pue Sainpaoid ay} yo Aue ul 10 8/84 paxOAu!
Z ainjuaapy ul se - uoN9a]}09 aq Aew wesBoid ay) Aq pasn yun Aue jo saoepajul
“WU 0} Saa}U! OU jo are inq ay} Ul pase|Dap SUOI}OUN) pue SaiNpad01g ‘swesBoOId
SaiNSe9J} ay] Waeas yey) $]9aIGo sjuasaidai sayey ‘ajdwexa 40) :s}oa!qo Jo [BOSE JOY}O Ul SB '}Suly PAYXOAU! S| YOOIG WesBoud UE ay L
Aiobajyeo jusiayip e sjuasaiday yoea—adA} saipoos au} jo sabueiqns NID3a
Apoodwwm ‘yses} ‘sayey
‘€ ainjuaApy u! S}oalgo |e Buyuasaidas adA} page oe suonouny pue ‘sainpasod
‘adA} sasap ay} jo abuegns e si adj} swioos ay Suonese|29P HWA “AdAL “LSNOD
‘€ SINJUBAPY U! SUOHEDO} au} BuljUasaidas adA} payesowinuy , :
swool Sl} AYVYEITWALSAS ‘so0| {apoo'soojng
“‘€ ainjueapy Aq pasn suoinduosap |je Buueseidas adA} payeuownuy ; 84} Ul Aq JSNW SJ9Y}O |IV ‘tspwo {apoo';spuiong
sosap JUBWWOD ay} Ul Palyloads ‘zspwa {apoa:zspuiang
Sl} BPO BU} Ul PUNO) ae ‘espwia {apoo'gespuiong
AdAL yt ng} ‘sqoud {apoo’sqoudng
WO} BY} JO SJUBWWOD Aq ‘eyepape {apoo: | yung }
30VINALNI paepeoeid esoy, ‘wesbod ‘ymsajdde s3sn
ayy Aq pasn syun jo ysI7
eyepape LINN quaApeyw WVYIONd
‘ESP LINN JO euIRNO ayy :E eiNUeAPY 404 aUIIINO apod aUL “p-Sz “Bly
"ESPWd U! Sa|qUeA au} JO} BPOO UOHEZHeENIUT
NI93¢
‘suolouny pue Saunpasoid soepaju! ay} Buljuawajdw! apo
*‘suonduosap algo jo wewwes6 au}
aAoiduwu! 0} saipoobmoys Aq pasn - saiqewea saipoob 40 14S
sueasn ‘sawos ‘sjeinjd
UVA
NOILVLNIW31dWI
*UB9|00g:(S91p003:}!)sa1poo3¥9 NOILONNA
Se See ‘sa1poo8:dnyoo}iqo NOILONN
Z ainjuanpy u! sv *(S9Sap asym) MOYS JYNGIIONd
‘yoojd 34NdII0Ud
(
(s}aafqomoys <+ saipoosmoys) { ‘saipoo3 mMoys 3YNd3II0Ud
90|3y9
aweupwo
112)
peay
& ainUsApY U! SV puewwoo
saoejd
ayeueu
OY yX
UWA
dasa0e|d
aul|A10}s
Z ainjuanpy Ul SV aweud
spwo
suonoasip
3dAL
3OVIUIINI
‘espwo LINN
“sqoud LINN JO eul|NO ay} :E auNjUeAPY 40} OUIIINO apod ay ‘E-
“ON3
NIS3a
“yun sqoid ay) Aq pauinbas uonezieniul on {
“Anoaaip
suoissasdxe au} Bulyenjead uey) sayyes ‘UOOUNY
SIY} 0} S|}ed BSN aunjuUsApe ay} JO SWed J9YyIO
“sjuauodwoo wWajqoid 0} Bulpuodseu0o suoissaidxe
Uuea|O0g ay} JO UOHEN|eAS au} 9Z1/e4]UBd 0} UO!}OUNY
‘ueajoog :(swajqoid :yoIym) Paros NOILONNS
‘ad Ay Swiayqosd 94} YYIM 4OI[JUOD PIOAL 0}
sqoid peweu Ss! jas}! uN ay, ‘Panjos si pajuasal
-dai wajqoid ay) asojaq ans) aq ysnw yeu}
uoissaidxe ueajoog e 0} Spuodsai0o adA} swajqoid
8u} Ul JayIJUBP! YORI *E ainjUsApY Ul SWa|qoid ay} 40
s}uauodwod ay} Bujuasaida ad/} payessuinug
swajqoid
3dAL
JIVINILNI
‘sqoud LINN
194
"LSPWO LINN JO eUINO ay} :E eiNyUeApY 40} BUI]NO epod a4 *9-GZ ‘B14 ——*S90}_ LINN PUB ZSPWO LINN JO eUI|NO ay} :¢ eiNjUeApy 10) aUIIINO apod eu “S-Sz ‘B14
“ONI
“o's, ‘u,] =? sueyop “uolyeo0} S,WWM
OY} PaysIA sey JaAejd ay) SAWN JO JaqUUNU ay} JUNOD oO} BiqeUe/A
NI938 ‘W293LNI ‘wwaye
“‘Seinpadoid soepajul ay} JUdWA|dWI 0} apOD VA
*suonoauip :AeMYSIYM NOILONNA
‘UVHO :puewWwos0P NOILONNA NOILVLN3W31dWI
Z ainquaapy ul sy somml auNd3I0ud ‘UONedO] S,uIeJUNOW ay} Jo
‘sa8aqut :asoosd NOILONNA UeW ASIM ay} Je UOOe ay} a|pueY Oo} ainpadolg
‘spwo :dnyoojpwd NOILONN
‘wud 34N0390Ud
"S|JO4} BY} JO SUOI}Oe pue snjejs
ay} SuoluOW swajqosdyd ‘VeEinoed f ‘uoNDe}01) 3yNGID0Nd JOVINILNI
u] “€ auNjUeApY JO SWa;qoid 3y} *y@9u}01) JYNGIIONd a
yo yoddns ul pesn ainpesoid ayeaug | ‘Swaiqosdy9 34NG3I0dd
"SPUBWWOD UO!DAJIP 0} puodsaJ09
yeu) Siay9| Bunueseidas— 13S YWHO ‘YWH9 4O 13S ‘sieyop
UWA
NOILVLNSW31dWI
“J@YJOUL 0} UOIEO| —
QUO WO4} |@ABJ} PUB UONe}UBWa|dW! PUBWWOD 10} SaiNpad01q
‘eppemd 3ungag0ud
‘djay 10} payse sey saAejd ay} saw Auew Moy yo ye) deay 0} aiqeueA
‘ynoysd JUNd3IONd ‘Y3931NI :payse
‘uadod 3ud390Ud
‘doupd 34nd390ud UWA
*Aueod 34Nd390Nd
*(SW00s:90)p'90jN‘90jM‘90]9‘D0}S‘I0|U) }aAes} JYNDIIONd NOILVIN3W3IdNI
J0VINILNI ‘djayd 3uNG390Nd
‘spud LINN JOVIUSLNI
‘wspwso LINN
195
‘wey} UseMjeq
suoldauuo0d abedyo pue ¢ ainjusApy 410} Sebed dew Buimous wesbeip y “g-sz ‘bi4
€1-S2 aunBi4
dew uyyi4
6-Sz eunBi4
dew jsul4
vi-s2 eunBiy
Ol-S2 eunBbiyg dew ujxis
dew puovas
1-2 eunBi4
dew pays
Zi-Sz2 aunbi4
dew yuno4
"€ SINUSAPY JO SHUN ay} JO} SUOHeja1 SeSN ey “Z-Sz ‘B14
yun
eyepape
JUSAPe}W
swesBoldg ule
196
cellar
fountain
(of youth)
Fig. 25-9. The map of Adventure 3: Page 1.
197
Fig. 25-10. The map of Adventure 3: Page 2.
the information that the unit provides to other parts
of the program. The implementation contains the
code for all procedures and functions of the unit.
The advantage of the unit concept lies in the fact
that any part of the implementation may be changed
without requiring the remainder of the program to
be recompiled. Also, other parts of the program
cannot use knowledge of the innards of a unit in
order to play programming “tricks.” This makes for
more robust code and fewer bugs. Chapter 27 ex-
198
plains the syntax of units and how to use them in
your adventure game programs. It explains the
interface and implementation concepts and talks
about linking units together to form a program.
Units are separately compiled from one another,
and so there is an extra step in preparing a program
that uses units. This step is called linking.
Chapter 28 delves into the problems im-
plemented in Adventure 3. It explains how a large
part of the representation of those problems is en-
carvings
berries
saddle1
cavela
Fig. 25-11. The map of Adventure 3: Page 3.
199
"g aBeg :€ ainjuanpy jo dew ey ‘e1-Sz “B14
‘y aBq :€ ainquarpy jo dew ay) ‘Z1-Sz “Bly
a)
s ‘p ze|ppes ae
M
n
pis
200
"€ eunjuaapy jo Swaiqoud ay “S1-Sz ‘B14 ‘9 eBey :€ ainjuaapy jo dew aut “y1-Sz “614
‘dn pauado si uinoA jo urejunoy
94} 0} Aem au} ‘op noA ua ‘PUBWWOD eB
se }| 8SN puke JeIj99 ay} 0} LUN}aJ SNW NOA
‘no 0} puom a1Bew ay} sjeanes WWUM 9u} Jay
‘puom o16ew au}
JE9Ae1 WLUM au} ||IM Ud} AJUO pue Udy “p
wajqoid Burajos saye uoled0) SWUM ay} WO
.YSeJ},, Je pue Saye}, |e SPAOWSI JsnW NOA
‘g wajqoid Buiajos saye
—B | aAe9d UOIe90} Je Y}OO} Sj}04} 84} Bulpuy
sapnjoul Siu, “Wwauy} sAea}| pue WWM UO!}ED0}
0} ,, SaipoobuwM,, au} je Bug ysnw no,
“‘souisap Ajns} WWM ay} yey} $}9alqo asou}
wod4 Sainsead} ,,ayej,, ysinBuljsip snus Nod
“9ABD BU} 0} Hulpes}
yoyey ay} uddo 0} JAapsO Ul JeI}9O By}
0} way} Aveo pue shay ay} aye90) JsNnW NO,
“JOABIOY
S|]04} 84} JO Pld aq UBD NOA ‘sauog au}
doup noA ji uayy “e} BAD O} Sj]O1} BY}
aun} 0} Way} asN ysnwW NO, ‘wWayY} Ae
pue a8Aed 9y} Ul S9U0g 9y} pul} JSnW NO,
“UO1}EDO} S|]O1} BU} Ye BAUIe NOA UayM Sau0g
ay} BurAueo aq jsnw no yeu} Sueaw siy,
*$|]04} a4} Aq uayea Bulag plone ysnw No,
201
capsulated in the program unit known as the probs
unit. The use of the solved function is discussed in
connection with this. The problems posed by the
adventure and the Pascal code used to implement
them are described. The precondition—post-
202
condition notation is used as it was in the descrip-
tion of Adventure 2.
Chapter 29 discusses the miscellaneous cod-
ing techniques used in the implementation of Ad-
venture 3.
Adventure 3’s description database file
can be found in Appendix C, page 292.
The Listings of Adventure 3
This chapter presents the listings of Adventure 3. units used in the program, as well as the main
There is a separate listing for each of the Pascal program.
LISTING 26-1. ADVENTURE 3 MAIN PROGRAM = Save file as “MTADVENT”, as per page 243
PROGRAM mtadvents
C$s+3
USES applestuff, These are “uses” instructions; see Chapter 27,
{tumti.code} advdata, specifically page 242 for more information.
{$uprobs.code? probs, aA , ;
{$ucmds3.code} cmds%, For these and additional “uses” instructions
{$ucmds2. code? cmds2. which appear on pages 207, 209, 224, 225, 233
{$ucmdsi.code} cmdsi, and 234, refer to “A note about disk Drive
{$ulocs.code} locs; numbers in file names and commands’on page 3
of this PDF.
PROCEDURE t1 (ll: rooms);
REGIN
CASE 11 OF
starts: travel (traili, trail4, nowhere,
203
204
trailis
trails:
trails:
trail4:
trailts:
trailé:
coli:
cola:
cols:
col4:
rubblels
rubble?s:
mound :
travel
travel
travel
travel
travel
travel
travel
travel
travel
travel
travel
travel
travel
monastery:travel
cellars:
Caver
slopels
slopes
slopes:
mut ds
ruts
ruts
travel
travel
travel
travel
travel
travel
travel
travel
trail3, nowhere, nowhere) s
{frubblel, start, nowhere,
nowhere, nowhere, nowhere) 3
(trailé, rubble2, nawhere,
trail&, trailé, rubblel)s;
(nowhere, nowhere, start,
slopes, nowhere, nowhere):
(start, mound, nowhere,
nowhere, nowhere, nowhere) 3
(nowhere, nowhere, trail2,
saddlel, saddlel, nowhere);
(saddle3S, trail2, nowhere,
nowhere, saddlest, trail2);
(nowhere, nowhere, saddlel,
nowhere, saddle2, saddlel);
{(slopeS, rut3, nowhere,
nowhere, slopes, rut);
(nowhere, nowhere, steeps,
nowhere, peaki, hilll)ds;
(nowhere, guillyl, nowhere,
saddlez, nowhere, quilyl)s;
(trail2, traili, rubblez,
nowhere, trail2, nowhere);
(nowhere, nowhere, nowhere,
rubblel, nowhere, nowhere) s
(trail4, nowhere, nowhere,
nowhere, nowhere. monastery) ;
(nowhere, nowhere, nowhere,
nowhere, mound, cellar);
(nowhere, nowhere, nowhere,
nowhere, monastery, Cave);
(nowhere, noawhere, nowhere,
nowhere, cellar, nowhere) ;
(nowhere, saddle2, nowhere,
nowhere, steepl, saddle2)s;
(nowhere, nowhere, mowhere,
nowhere, steeps, trolls);
(steeps, col2, trails,
chasm, steeps, noawhere) :
(nowhere, nowhere, ridge,
trolls, nowhere, nowhere)
(nowhere, nmowhere, ridqgel,
nowhere, nawhere, nawhere) §
(col2, nowhere, nowhere,
nowhere,
cole,
nowhere) §
END {¢ CASE 11 OF }
22
END { PROCEDURE t1 33
PROCEDURE t2 (ll: rooms);
BEGIN
CASE 11 OF
steepl: travel (nowhere, nowhere, chasm,
nowhere, wmm, slopel)s;
steep2: travel (nowhere, nowhere, chasm,
nowhere, peak2, slope2)s
steep3s: travel (nowhere, nowhere, nowhere,
chasm, peaks, mtcave2c)s
steep4: travel (nowhere, nowhere, chasm,
nowhere, peak4, ridgel)s
steept: travel (peakS, slopes, nowhere,
chasm, peakS, slopes);
peaki: travel (nowhere, nowhere, nowhere,
chasm, nowhere, col);
peak2: travel (nowhere, steep2, chasm,
nowhere, nowhere, mtcavelc)s
peaks: travel (nowhere, nowhere, nowhere,
chasm, nowhere, steep):
peak4: travel (nowhere, chasm, nowhere,
nowhere, nowhere, steep4);
peakS:s travel (nowhere, nowhere, chasm,
nowhere, nowhere, steepsS) :
saddleil: travel (trolls, nowhere, rutl,
coli, trolls, mtcavela);:
saddle2: travel (slopel, coll, steep2,
ridgel, slopel, coll);
saddle: travel (nowhere, trailéd, col4,
nowhere, hHilli, traild)s
mtcavela: travel (mtcavelb, nowhere, nowhere,
nowhere, trailS, nowhere) ;
mtcavelb: travel (mtcavelc, nowhere, nowhere,
nowhere, nowhere, nowhere);
mtcavelcs travel (nowhere, mtcavelb, nowhere,
nowhere, peak2, nowhere);
mtcaveZa: travel (mtcave2b, nowhere, nowhere,
nowhere, qully2, nowhere) s
mtcave2b: travel (mtcave2c, mtcave2Za, nowhere,
nowhere, nowhere, nowhere) ;
205
MECAVELC:?
gullyi:
gully:
hills
trolls:
ridqels
ridge2:
Wwinms
chasms
END ¢
END € PROCEDURE t2 33
BEGIN
writeln
writeln
show
IF ord
THEN
ti
ELSE
t2
travel (nowhere, mtcave2b, nowhere,
nowhere, steeps, nawhere)
travel (cal4, gully2, nowhere,
nowhere, col4, gully2);
travel (gullyl, nowhere, nowhere,
nowhere, gqullyl, mtcaveta);
travel (nawhere, nowhere, nowhere,
nowhere, cols, saddles);
travel (slope2, saddlel, chasm,
nowhere, slope’, saddlel);
travel (nowhere, nowhere, saddlez,
rut2, steep4, nowhere) ;
travel (nowhere, nowhere, saddle?,
nowhere, nowhere, nowhere) ;
REGIN
pwmms
travel (nowhere, nowhere, nowhere,
nowhere, nowhere, steepl);
END s
BEGIN
done := true:
killed = trues;
END:
(location)
(location)
(location)
{ END IF ord ...
UNTIL done;
206
CASE location OF 3
ie “Adventure” and “There”
(‘adventure 3 begins..." )3
(*there are
repeat t
visitedClacatioan]
(location) ;
*, memavail, °® bytes of memory
:= visitedClocation] + 1s
“ ord (steepl)
*
+s
left. ");
END.
LISTING 26-2. LOCATION PROCEDURE UNIT
Ds sus wa tn aS sss bn shen aks nsx ie nena Sw: HO Sih a tn hc one th Stns st un oie
{
{ Il ec uiom of t
if
{ the locs unit cantains procedures needed for
{ handling special situations at certain game
{ lacations. in mtadvent the anly such place
{ is “‘wmm". therefore, this implementation of
{ locs cantains anly the procedure pwmm.
if
Lm sa scion sine shtin eenbe, Fae wots boeee ane: Sone es eer so ‘etononeeb seece. oped Stone nenee saben oosin enews aatis beens Stee Gente Seceh sitnh ene ceeed sneha Sniun Stans sense snens
{$S+3
UNIT locss
INTERFACE
USES applestuff,
{tumtil.cade} advdata,
{$uprobs.code} probs,
{$ucmds3.code} cmdst,
(S$ucmds2.code? cmdsez,
{$ucmdsl.code} cmdsl;
FROCEDURE pwmm;
IMPLEMENTATION
VAR
atwmms INTEGERS:
ae te met nee te me er nee ee se te tn st tne sn ee he at me mn ns eee se ena wen Sonn Goan tose abt es eee esane
za
=
3
3:
wt bs es be
location wmm.
%
£
{ handle special occurrences at the
:
4
{
j
he he he,
Hy
H
i
$
1
H
$
PROCEDURE pwmms;
VAR
rs INTEGER:
RJ RS he OS RS RS he Re Re ke he
207
BEGIN
atwmm 3: atwmm + 13
IF solved (wmmhappy)
THEN
show (wmmbl ab)
€ END IF 3;
IF wmmcount 3 3
THEN
show (wmmharp)
{ END IF 35
rose rand (1,5);
CASE r OF
show (wmmspl);
show (wmmsp2)
show (wmmsp 2) s
show (wmmsp4) ;
show (wmmsps) 3
Chie
END < CASE r OF 33
END { IF atwmm > 1 35
IF atwmm = 1
THEN
show (wmmhella)
€{ END IF 33
END ¢ PROCEDURE pwmm 3};
BEGIN
208
LISTING 26-3. COMMANDS 1 UNIT
source file: cmdsl.text
he she ah
for the name of the cammand,
all located in this unit.
Ar eer Ss eM ee ee I eR eT eS
in the interface to cmdsil.
re ee ee rn
{$+}
UNIT cmdsis
INTERFACE
USES applestuff,
{fumti.coade? acdvdata,
{$uprobs.code? probs,
{$ucmds2.code? cmds2,
{$ucmds3.code? cmdsis
FROCEDURE travel (
nloc,
this unit contains all command processing
support pracedures and functions. each
command in the adventure is carried aut by a
procedure called ptcmd>, where
AS
player: @.qg. pcarry “===> carry command.
same of the ptcmd> procedures are located in
this unit, the others are in cmds2 and cmdss.
procedures and functions like travel, noway,
docommand, listen, and cmdlaokup which are
invoked in order to recagnize the command are
the procedures ckproblems, trolmeal, and
trolaction are also located here in order to
be accessible to the appropriate command
processing code. none of these procedures is
tcemd> stands
typed by the
Be wh oS ke ke be ks Le ts
LS kw bs bet bh he ke
BS tt het tt
Ls ie
j
|
|
i
cmds3 must be listed before cmds2:
{Sucmds3.code} cmds3,
{S$ucmds2.code} cmds2;
209
sloc,
eloc,
wloc,
uloc,
dlocs rooms);
PROCEDURE pcarrys
FROCEDURE pdrops
FROCEDURE pshout;
FROCEDURE popens
PROCEDURE pwaddas
IMPLEMENTATION
VAR
dchars: SET OF chars
Cc k p r Q b 1 e m s
see if the player has salved any problems
because of the command just executed.
et het et A ht Re
ea ee we ot
FROCEDURE ckproblems;
BEGIN
IF (solved (bonetrolls)) AND (salved (luretralls))
THEN
BEGIN
show (tgabbones) ;
trlives := false; { make “em disappear }
whatshere(Cmtcavelal :=
whatsherel(mtcavelal] - Cboanesd;
END ¢ IF (solved (bonetrolls) ... 3
IF (NOT trlives) AND (location <> mtcavela)
THEN
whatsherelmtcavelal] :=
whatshere(mtcavelal + Ctoothd;
END ¢€ FROCEDURE ckproblems 33
210
{mer ce a me met es cee cate ee eect ce at an ee ne ee ste ee es tt ne ee te ne ss ey
{ t r Q 1 m e a 1 3
£ +
{ print the description of the player }
{ being done in by the trolls and }
{ exit the program. 3
{ sence soe wasen costa soene ene samme suees ene nnse saute cones ntees wumm sane sense meet snsen some Samee sent canes sess non ene teuse comme sense rem sous sanee cert onnte sone seme cece capes }
PROCEDURE trolmeals;
BEGIN
show (trtalk)s
exit (PROGRAM) 5
END { PROCEDURE trolmeal 33
£
t t r QO ] a Ce t i oO n
{
{ handle the trolls and assaciated problems.
{ detect the first encounter with the trolls,
{ moniter the trolls following the player when
{ the player is carrying the bones. check for
luring the trolls to mtcavela.
rs
t
Lasse sence sssse svsne sasen sopee coset suman acces cece seses sotse oesce coven ovum evens ovens seuss saeet ennee steen erste stats seme eeeee sures ceeee anent seete seman arate stame svees svete eeeee omnes satan mane sueee crane comme seers seuss sues cases stems seuee
%
FROCEDURE trolactian;
BEGIN
IF (location = trolls) AND (troltime = ©)
{ first encounter 3+
THEN
BEGIN
IF bones IN stash
THEN
REGIN
troltime := turns;
{ solve luretrolls problem 3}
show (trhungry)s
END
ELSE
trolmeal
{ END IF bones IN stash 33
END ¢< IF (location ... }
ws ht ht ted LS RS ee
Ls bs
211
The condition “(location <> trolls)” was
i aalvwd Cureteot te) probably added to prevent the message
THEN
IF -Cheeetrern— tre —AND—
fhenen Ih atach’ Instead this condition causes the trolls to eat
the player if they return to the “trolls” location
—— fr Fol i Ge) while luring the trolls, which is confusing and
ELSE makes the game unnecessarily difficult.
trolmeal aa ; dacsiaeet
? END TF (Cloeation .. 3 * Page 248 indicates that if the player is luring
€ END IF solved (luretrolls) }; the trolls and hasn’t figured out how to
dispatch them after 25 moves, the trolls will
IF (location = mtcavela) AND
ecived tluretrolia) AHD code to handle this; instead the trolls disappear
(hanes Th wean) and the game becomes unsolvable. One
THEN solution is to add code here which checks
show (tgablure) IF (troltime<>0) AND ((turns-troltime)>24)
€ END IF 33 THEN kill the player. (On the included disk
e —% image, | wrote code to display a short text
END { PROCEDURE trolaction 3: passage, which segues into “trolmeal” which
‘ displays more text and kills the player.)
men mae me eee me tm mane tse se me i et en ne re tn aes en nt ens se te "Ye
t c m d 1 Q Qo k u p
{ determine which cammand the player has typed }
{ and dissect the cammand string into *head’ 3
{ and “tail’ far cammand verbs with abjects. 3
[Tite sttee sees mun tate eee sno mate stim ate pvme see nn cena ett cutee ete ate tne sean van teen ant see sat tim ste ma sent sate tens sees thee Stns suet sens vente eee ota snes oe tute Meat snes ana eee }
FUNCTION cmdlogkup : cmdsy;
VAR
p: INTEGER:
lemd: cmdss
BEGIN
writelns Capitalize ‘Your’:
write ( yaur wish is my cammand> *)3;
readin (command) ;
po r= pos ¢(* *, command):
cr
€ check for verb-object }
IF p = ©
THEN
BEGIN
head := command;
212
“trfollow” from being printed immediately after
“trhungry”, but it’s not needed if that’s the case.
“have some fun” (eat the player.) There isn’t any
END
ELSE
REGIN
head == copy (cammand, 1, pri);
tail == copy (command, pti, length(command) —-p);
cmdnamelnocmd] :
lemd :
WHILE head <> cmdnamelClemd] DO
lemd := suce (lemd)
{ END DO 3,
cemdloakup #= lemds;
END ¢ FUNCTION cmdlookup 3;
Pp S c o r &
calculate the number of points scored by the
up to this point in the game.
a ae oe i eS a
ee ed
FUNCTION pscore : INTEGER;
VAR
re rCMaMms §
keepscore: INTEGER;
BEGIN
keepscare = Of
FOR ros start TO wmm
DQ
IF visited(irj = 0
THEN
keepscore := keepscare + Sy
IF saidwadda
THEN
213
keepscare
IF location =
THEN
keepscore
pscore := keepscore;
END { FUNCTION score
:
3s
i
j
I
{
i
|
|
1
i
H
i
i
i
!
j
H
i
i
!
i
'
I
ei
i
-.
&
procedures
me cee es es
PROCEDURE listens;
VAR
lemd: cmds;
FROCEDURE Ilscores
BEGIN
writeln
writeln (pscore,
END {¢ PROCEDURE Ilscore
FROCEDURE lquits
VAR
chs CHAR:
fhold response
BEGIN
writeln
readin (ch) 4
IF (ch = *y*)
THEN
BEGIN
writeln
exit (FROGRAM) ;
END {IF (ch = “y")
END { PROCEDURE lquit
OR
BEGIN
214
fountain
or
keepscore
d
keepscore +
(S50 -
(fountain)
+
bh
mi)
dispatch calls to the command executian
( ptemd?> as described in the
header comment for this unit.)
Capitalize where indicated by arrows
("if you should quit now,
your”
- 24) 5
wo ks
Le ed
Rs hw bet
score
points of a possible 350.
350 isn’t the highest possible score,
but I’m not sure what the correct number would be
(ch
+
2
a8
y
35
3
for quit confirmatian?
Care you sure you want to quit?’)s
|, Capitalize the second ‘Y’
Fa y y
(you would have scared
pscore,
would be *)
me
points. *)
REPEAT
turns + 1s
turns :=
:= cmdlookups;
lemd
CASE lemd OF
take,
carry: pearrys
drops: pdrop;
help: phelps
invent: pinventorys
SCOre,
tally: lscores;
looks plooks
shouts pshouts
open: popens
unlocks popens
waddas: pwaddas
quits Lquits
nocmd:s: .
END { CASE lcemd OF 33
ckproblems:
{see if any problems were solved by the
€{ command issued by the player.
het bs
UNTIL. lemd = nocmds;
END ¢€ PROCEDURE listen 3};
A ae as ea te pa a re eh ce Se a ery ar em
{ d Ci) c Oo m m a n d 3
{ }
{ call listen in a loop. when the length of 3
{ head is greater than zero, the user has given}
{ a travel command so return the first letter }
{ of *head* to the caller. }
ca
1
i
|
i
|
i
I
i
!
i
i
i
!
i
i
i
i
i
!
!
|
1
!
I
i
i
!
i
i
1
|
|
i
!
|
i
I
{
i
i
i
i
i
i
{
Ld
FUNCTION docommand : CHAR;
BEGIN
215
head re Tg
tail ga FF
chgloc := false;
REFEAT
listens
UNTIL length (head) = O;
docommand := headlid;
END € FUNCTION docommand 33
Fe an BO oe SO a i
i
i
j
H
i
i
i
H
i
i
i
i
{
i
i
i
i
H
w a
FUNCTION whichway : directions;
VAR
ch: CHARS:
ww: directions:
BEGIN
REPEAT
ch := docommands;
whichway i= x4
CASE ch OF
*n’s IF (head =
THEN
whichway
si lb IF (head =
THEN
whichway
Ze": IF (head =
THEN
whichway
Pw" s IF (head =
THEN
216
*n*) OR
r= Ms
*s*) OR
r= S5
*w’) OR
(head
(head
(head
(head
determine which way the player wishes to go.
=
Be Rt RY Le
ue
*north’® )
*south’ )
*"east”)
*west”*)
whichway '= ws
urs IF (head = "u’?) OR (head = *up*)
THEN
whichway := us
"d's IF (head = “*“d*) OR (head = *“down*)
THEN
whichway := d3
rary { empty for now 33
END { CASE ch OF 33
UNTIL ch IN dchars;
writelns;
END € FUNCTION whichway +3
{ casas sone sev snste sneen ess evese snmp cosee seen onsne canes seeve mse costs cues ettoe comes soceg seme seats ouom sbmne tues meer cosee aneee suse eumnt ceeee tress stems seman ean sete enme aenes sanen seene mes sees stems se
¢ n o w a y
{ secs canen esane seus cones seen esos tenes asses some puree costs sort sete sete ese estes sewes cages sates tonne sates stone mune wees tose crabs coten sttee cious suse sunse seme tune crest ceuoe cuten apne samen enene tener comes muse sees senee eee
FROCEDURE noways
REGIN
writelns; J Capitalize ‘It’
writeln ("it is impossible to go in that direction.’ );
ch@loc := false;
END ¢€ PROCEDURE noway 338
tee et WS RS RS Re bk ee be ee ke es es
Ls mee te ete seme ce eens te te ene et ee tn ten tn em me mnt tn a ee en ts
t ° a Vv e 1
handle travel ta the next location. the
possible destinations from the current roaom
or adventure location are passed to travel
parameters. the value “nowhere’ means that
there is no way to go in the corresponding
direction. there is special case code for
direction “d* oar “dawn’. the location
*cellar’ has two possible destinations in
that direction, one of which is the winning
location.
ears ee ee es ee es ee of es 8
217
FROCEDURE travel;
FROCEDURE newloc (loc:descs);
BEGIN
IF loc = nowhere
THEN
noway
ELSE
REGIN
=
lacation =: loc;
chgloc ee trues
END ¢{ IF loc = nowhere }+3
END ¢{ FROCEDURE newloc 33
FROCEDURE wingames
alla J Capitalize ‘You’
show (fountain) s
writeln ("you scored a total of *, pscore);
writein (* points out of a possible 350.")35
exit (FROGRAM) ; 350 isn’t actually the highest possible score,
but | don’t know what the correct score is.
END ¢ FROCEDURE wingame 33
BEGIN CKKKKK to or oa vee J] *kKKK}
IF (lacation IN trollacs) AND trlives
THEN
trolactians
CASE whichway OF
ns newloc (nlac);
Ss newloc (sloc)s
e: newloc (eloc);
wr newloac (wloc);
us newloc (uloc)s;
d: BEGIN
IF location = cellar
THEN
BREGIN
218
IF saidwadda
THEN
wingame
ELSE
IF isopen
THEN
newloc (cave)
ELSE
noway
{ END IF isopen}
€ END IF saidwadda 3}
END
ELSE
newloc (dloc)
{ END IF location = cellar 3;
END { case ds: }3
M REGIN ne Capitalize where indicated by arrows
wO3
writeln (7i do not understand that.’);
writeln (*please try another command’);
END; t
END ¢ CASE whichway OF 33
END < PROCEDURE travel 3;
i
i
f
!
i
1
i
i
!
i
H
i
i
!
i
I
j
i
D
n
cr)
os
5
<
implement the carry command. call the
function “abjlookup* to determine which
object, (if any) has been requested. then
call “ckqoadies* to see if that object is
present in the current Location. if so, the
object is added ta the set “stash* and also
removed fram the set “*“whatsherelLlocationI’.
Che he he Se ae he al he Dl
i
PROCEDURE pearirys
VAR
it: goodies;
BEGIN
it #= objlookups
IF NOT ckgoodies (it)
THEN
hes et 3
he Be he be be we
Rt ket
219
BEGIN J Capitalize where indicated by arrows
write ("i don’*t see any “*)s
write (tail)s
writeln (° here.*)s
END
ELSE
REGIN
writeln (’ok*)s
stash := stash + Citds
whatshereLlLlocation] :=
whatsherelLlocation] -— Citds
IF (location = wmm) AND (it IN wmmfakes)
THEN
wamcount #= wmmcount —- 1
€ END IF },
END ¢ IF NOT it IN ... F8
END ¢ PROCEDURE pearry 33
nd
es
Bt eet he he
implement the drop command. similar in
actian ta the carry cammand (q.v.).
ro es tk
he
PROCEDURE pdraps
VAR
it: goodies;
BEGIN
it = objlookups
IF NOT (it IN stash)
THEN
REGIN |
write ("you are not carrying any *);
wreiteln (tail);
220
END
ELSE
BEGIN
J Capitalize ‘Ok’
writeln ("ak"):
stash := stash ~ Citids
IF (location = wmm)
THEN
REGIN
IF it IN wmmwants
THEN
wmmhas #= wmmhas + CitJ
ELSE
whatsherelCwmm] := whatsherelCwmm] + Citd
{ END IF it IN wmmwants 5
IF it IN wmmfakes
THEN
wmmcount s= wmmcount + 1
€ END IF it IN wmmfakess 3
END
ELSE ;
whatsherellacation] :=
whatsherelLlocation] + Cit]
{ END IF (location = wmm) 33
END ¢€ IF NOT it IN stash 3;
END { PROCEDURE pdrop 33
{ caine ‘ints cia Geen osven Sven cuss soe basen S600 oncte Stun saves wanes cates come exten bese Gens euesd sutou'conee oauee snsbe'ounee soese Goes este Seorysebts covet vores <s000 conte wwere Suueé see seen esGwsinbere senen ree? eines stone exes wants }
provided the keys are being carried.
Cie oe che ae Se
FROCEDURE popens
BEGIN
IF NOT solved (canopen)
THEN
handle the open command. the player can
apen the hatch to the cave under the cellar
221
Capitalize where indicated by arrows
writeln (you can**t open anything here!’”)
ELSE
REGIN i
writeln (*ok.")s
isopen := trues
writeln (’one of the hatches is now open. *);
END ¢ IF NOT solved ... 33
END ¢ PROCEDURE popen 33
p w a d d a
handle the wadda command. in order to win,
the player must say *“wadda* at location
cellar. this can only happen after the wmm
problem has been solved.
eee mM et
Bo by BS We be be bs
FROCEDURE pwaddas
BEGIN
IF NOT solved (ftofyouth)
THEN
writeln (?i don’*’*t understand baby talk.*)
ELSE
BEGIN
saidwadda := true;
writeln (* you are clase to the secret.”)3
t
END { IF NOT solved ... F8
END { PROCEDURE pwadda }3
Shouting doesn’t affect the adventure at all,
PRBEEOERE iPeEeuys but you could add your own routine if you like
VAR
is INTEGER;
je INTEGER;
BEGIN |
writeln (’ok.")3
FOR i s= 1 TO Soo DO
Capitalize ‘Ok.’, ‘A’ and ‘There’
write ("’a*)s
FOR j 2s= 1 TO 100 DO
END;
writelns
FOR i = 1 to 15 DO
BEGIN
write ("qQ")¢s
j s= 1 TO 100 DO
write (7 !")s
FOR j := 1 TO 100 DO
writelns
FOR i s= 1 TO S00 DO
writeln (? there - now i feel much better.’ )43
END ¢ FROCEDURE pshout 33
BEGIN
dchars :=
C’q’s rn’, "Ss", -e*, ew. Ture 7d’, *y 7 73
‘q’ is unimplemented; see page 217
END. ‘x’ is a delimiter, aka “sentinel”
LISTING 26-4. COMMANDS 2 UNIT
oo
223
Cie sie a ake
{s+}
UNIT cmdsis
INTERFACE
USES applestuff,
{#umtl.code} advdata,
{$ucmds3.code} cmdss;
PROCEDURE phelps
IMPLEMENTATION
VAR
asked: INTEGER;
FROCEDURE phelps;
BEGIN
IF asked = 2
THEN
ELSE
iicalenih J Capitalize ‘Help’
writeln ("help is on the way’)s
asked := asked + 1;
END;
END { PROCEDURE phelp 33
BEGIN
asked s:= QO;
END.
224
this unit contains the procedure phelp. the
help command needs no access to data in unit
“advdata’*. ait does use procedure
however, and hence USES unit cmds3.
"show" ,
lod tad
ht tt ie
The Adventure 3 description database in Appendix C
h the! . € contains no “helpspiel” entry. This causes part of the “start”
alas il sia deine description to be displayed instead. To fix this bug, add your
own helpspiel as the final entry in the database (see page 303.)
LISTING 26-5. COMMANDS 3 UNIT
¢€ Vase Ss ass gee Soot be Sntcn Bison Sans even Serbs pes ees sav Stan sce SG sears die csc scene aose she, Sob tte ioe fee }
{ source file: cmds2.text
€ sis wwene eee oahay Sue nee epoca eens ince ua pa Seve Sescs oes pe Sep oan tee rin eed ets “ne op thcan eben eal Abickes }
{—-- Seep “eat ge ent: gen Sass nabs 'stbes ams v0 saoes anes petites dnien oem soot 900n8 ein esees vine Waren, t ee ests teed Same Goare eerie ene Sand sSbthomsee canted Sones mom Ys
{ c¢ m ds 3 uom ait 3
t 3
{ This unit contains procedures implementing 2
{ cammands. The particular commands implemented?
{ herein need access to the unit advdata, but >
{ not ta the unit “probs’. +
{ 3
Lome ie SiGe tai eis evan TD cia py Game SEAN on Tere sc eas scans abe eae pare yes is SOE Si aS gacnn 6A GUS SCE <8 ata mabe eg SESS Si SoS Ga ca ee SERS oo}
{$+}
UNIT cmdsiys
INTERFACE
USES applestuff,
{fumti.code} advdata;
TYFE
directions = My Se O@aWa lla aX) 8
cmds = (carry, drop, help, score, invent,
take, tally, look, shout, open,
unlock, wadda, quit, nocmd); —
pname = STRINGC4OIs;
storyline = STRINGLBOI;
byte = O..2553
whichsect (indexsection, descsection) ;
placerec =
RECORD
CASE section: whichsection OF
225
indexsection: ( tableentrys: INTEGER) s
descsection: ( name: pname;
id: INTEGER;
dbegin: INTEGER:
dend: INTEGER;
links byte);
ENDs
VAR
xfiles: FILE OF placerecs
narrate: FILE OF starylines
places: ARRAY Cdescs] OF placerecs:
cammancds: STRING;
heads: STRINGS;
tails STRING;
cmdnames ARRAYCcmds] OF STRING;
chaloc: BOOLEAN;
{ has player maved since last cmd? }
PROCEDURE pinventorys
PROCEDURE ploaks
FROCEDURE show (where: descs) ;
PROCEDURE showgoaodiess:
FUNCTION ob jlookup: qoodiess
FUNCTION ckgoodies (it: goodies) : BOOLEAN;
IMPLEMENTATION
VAR
rs descs;
Plurals: SET OF goodies:
SOMeS: SET OF goodies;
UsSeanss SET OF goodies:
£
{ for benefit of showgoodies }
t
‘
i
S h Oo w
Retrieve descriptions from the database
and display them on the player’s console.
ea ce et e's
ht bt os Rs LS
226
The argument to show is of type “descs’
which includes descriptions of situations
and spoken words as well as descriptions
of lecations. Show uses the ord function
to detect what kind of description is
involved. The description of locations
suppressed if the player has not changed
locations or if the location has been
visited recently. If the player has not
changed location, then nothing happens.
If the player has visited the same place
recently, then just the short description
is displayed.
Bt bee bt es Re bs RS be be be ke te ne
AAA A eI Me ee ee cl es
PROCEDURE show;
VAR
i: INTEGER;
BEGIN
IF (chgloc) OR
(ard (where) = ord (nowhere)?
THEN
BEGIN
IF (visitedClacatian] = 1) OR
((visitedClocation] MOD 4) = ©) OF
ford (where) = ard. (nowhere) )
THEN
REGIN
WITH placesEwherel] DQ
BEGIN
FOR i := dbegin TO dend ba
BEGIN
seek (narrate, i) 3
get (narrate)s
write (narrate™);
END ¢ FOR i s= wen 8
END ¢ WITH placeslEwherel] 3};
END
227
ELSE
BEGIN dso
ui Capitalize
write ("you are *)3;
writeln (placesC€wherel.name) ;
END ¢ IF visitedClocation] = 1...
rd
“as
END {¢ IF chglec 33
IF ord (where) “< ord (nowhere)
THEN
showgocdiess;
END {€ PROCEDURE show 33
Arm
3)
a
ia}
=
0
o
ra)
oa
e
HH
fi
ke bs te
{ Print a list of the objects present at the}
{ current adventure game lacatian. }
me eee ee te me ee ee et ne ee te ee tam et msn esr at Ae at ey tse ee ys nee ne me "H
PROCEDURE showgoodies;
VAR
labj: goodies;
BEGIN
FOR lobj = nuggets TO noabj DO
IF labj IN whatsherellocation)
THEN
REGIN
IF lobj IN plurals
THEN
write ("There are same *)
ELSE
IF lobj IN somes
THEN
write ("There is some *)
ELSE
IF lobj IN useans
THEN
write ("There is an *)
ELSE
228
write ("There is a “*)
{ END IF lobj IN useans }
< END IF lobj IN somes }
{ END IF lobj IN plurals 33
write (abjnamellobjJ);
writeln (* here.”)s;
END {¢< IF lobj IN whatshere... 3
{ END FOR lobj s= wae 43
END ¢ PROCEDURE showgoodies 33
mmr mee ee nee ee te reste see tn ans tn ate me ee an ms ste see ue see te ae ae aan smn me te ee ar mae en ete ein eens evens canen enane oats -}
{ Q b j 1 Q a k ul p }
if +
{ Determine if the name typed by the player ?}
{ in a carry or drop command is the name of }
{ any object actually part of the game. If 3
{ so, return the internal value of the }
{ object in question. 3
et cee ee es mee ene ese ee toes ees ese ees a tn me tee i wens meee wns mms me tee a es sem seme mn mn sae seme
FUNCTION objlookups
VAR
lobj: goodies;
BEGIN
ob jnameCnoobj] s= tails;
lobj := nuggets;
WHILE tail ¢2> objnameClobjJ] DO
lobj s= suce (lobj);
objlookup := lobj;
END {€ FUNCTION objlookup 33
Lone 05 Sena oasin res etebe wets senon nes ese sctba sanio soins tule Speen sovee Ses osee bueah Sines wevesicoese sous less Se0es coten shane upon on sn em ne ene eens ete seme tne att es see ee ne
{ Cc k q c Q d i e $8 }
{ 3
{ See if an object is present. 3
FUNCTION ckgoodiess:
229
BEGIN
IF it IN whatsherellocatian])
THEN
ckgoodies :F true
ELSE
ckgoodies := false
{ END IF it IN waa 335
END ¢ FUNCTION ckobject 33
p 1 Q QO
Cie SE ote he
:
rae es
PROCEDURE plook;
VAR
savchgs BOOLEAN;
savisits INTEGER;
BEGIN
gsavchq = chgloac;
chgloc := true;
Implement the lock command. This forces
out the full description of the current
location. The variables chgloc and
visitedClocation] must be temporarily
reset in order to accomplish this goal.
Savisit := visitedClocationd;
visitedClocation] := 13
show (location) s
visitedClocation] := savisit;
chgloc := savchgs
END { PROCEDURE plook 1};
{ nmos sates ssove soone senee sonrs frome sneee temp sume cose ene tues seams sense sesee Some oneer steno teens seete sence sents Sener sunue arene snes mene neues senet sates seems tents sewuh ae seu oente tutte Heute anaes eaves seine Hines
{ p i n v e n
t
<{ Implement the inventory command. Print
{ the names of all objects carried by the
{ player.
{ Ah eoabb es ete seat cee ol sain Sinn nS aes dn sess ress bins opts tm nies iso’
230
Ret at RS
St bs bet es Re
Rt best he ks be Oy
ot
FROCEDURE pinventoary;
VAR
lobij:s goodies:
BEGIN
IF stash «3 (7
THEN
BEGIN
writeln ("You currently halds *);
FOR lobj s= nuggets TO noobj DO
BEGIN
IF lebj IN stash
THEN
writeln (cbjnamellobjd)
{ END IF 35,
END < FOR lobj := }
END ¢ IF stash <> C1] 43
END ¢ FROCEDURE pinventory 33
BEGIN
plurals := Cnuggets, diamonds, carvings,
wineskins, flowers, keys,
antlers, talons, feathers,
eggs, cymbals, bones, berriesd;
somes := Cwine, amethyst, flint,
incense, silver, leather]; €—— add beryl’ to this list
useans := CLaxe]; €—— add burner’ to this list
cmdnameCcarry f= "carry’ 5
cmdnameCdrop J r= "drop";
cmdnamelChelpd 2:= “help’;
cmdnamelinvent J := “inventory’s;
cmdnameCtally] = "tally";
cmdnameCtaked = “take”;
cmdnamelCscored = *score’s
cmdnamellookd = "“Look’s;
cmdnamelC shout J r= *shout’;
cmdnamelopend r= open’;
cmdnamelLunlock] r= Funleck’*;
cmdnameCwaddal r= ?wadda’s
cmdnameCquitd = * quit’;
cemdnameCnocmdd 2= “sentinel’;
231
reset (xfile, “mtadv.x*)
reset (narrate, “mtadv’)
‘ze ae
“mtadv.x” and “mtadv” are the database files used
by Adventure 3 for descriptions.
ror= start; Their location on disk is hard-coded into the game, and
seek (xfile, 31)3 Pascal will only look there. As written, Pascal expects
get (xfiled; these files to be on the disk in Drive 1. See “A note
placesEri] := xfile*; about disk Drive numbers in file names and commands”
on page 3 of this PDF for more information.
REPEAT
roe suce (rds
qet (xfile)s;
places(r] := xfile:
UNTIL r = helpspiels
close (xfile)s;
chgloc := trues
{ Force aut description of start }
END.
LISTING 26-6. PROBLEMS UNIT
i
t
i
i
i
t
i
Hy
this unit cantains the single function:
solved. solved is passed an argument of type
"problems’. the argument corresponds to one
of the boolean expressions used to detect the
solution of problems and components of
problems. the rest of the adventure code
uses code like:
i he ah te a SE ake eS al
Be RS RS ee ke ee
IF NOT selved (luretrolls)
THEN
instead of using the complex expressions.
the details of the problem expressions are
contained here and may be changed without
forcing the rest of the adventure game code
change as well.
Che a he Me on Me le he Se Sl ee a
Se
UNIT probss
INTERFACE
USES applestuff,
{tumtil.coade} advdata;:
TYPE
problems = (wmmhappy, luretrolls,
bonetrolls, canopen, ftofyouth) ;
FUNCTION solved (which: problems) : BOOLEAN:
IMFLEMENTATION
FUNCTION solved;
BEGIN
solved := false;
CASE which OF
whmhappy : solved :=
(wmmhas = wmmwants) AND
(whatsherelCwmm] * wmmfakes = £1);
luretrolls: solved :=
(troltime * © ) AND
CCturns ~- troltime) = ©) AND
C(turns - troltime) < 25);
banetrolls: solved :=
bones IN whatsherel(mtcavelal;
canopens: solved :=
(location = cellar) AND
(keys IN stash) s
ftofyouth: solved :=
(wamhas = wmmwants) AND
(whatshereCwmm] * wmmfakes = (£3)
(lacation = cellar);
AND
233
END ¢ CASE which OF }
END ¢€ FUNCTION solved >
BEGIN
END.
LISTING 26-7. ADVENTURE 3 DATA
Seana soos senha Sine iat eet asestachs'snean ins nbeah acne’ ste nb Sosis Sebo obi Soe ede se gees boson cveve' doen vies We
. 2
{ source files mtl.text 3
Gy secs sabss toca te er we tet Sg sae avon Sebel arb! sak eel sb Sse oS00sSoeh tice hint oaaed SHOE HET: PO
£ 3
¢ scott esos svgnninab Stile, nes Soesb Have pa’ sess deep icieod Asbo iv es ibs bavba noe, bet slanb tps taot asa. an ished ducae anh dsn a¢ec codes pens desi etn been dnt ceTnobge weben babs enbns ie (esessisvand one }
£ }
{ ro OF mM 8 “wom ait 3
£ he
. 7
{ types and variables used by all other units }
{ in mountain adventure. this includes the 7}
{ descs enumerated type, extended to account 3
{ for some descriptions that are not actual 3
{ game locations. the goodies type accounts }
{ for all objects in the adventure and the }
{ objname array contains names for all of 3
{ them (for use in descriptions and in 3
t commands). >
t 3
E airosegy eo reassess 60m Sitah Ses CSG es a aisb a Ui aS aba cepa Scones inet ak Sabon eS deat aca ats gs abet soe iweb Heese Saw sande est spitardeessi dea
tL a
($e+3
UNIT advdatay;
INTERFACE
USES applestuffs;
TYPE
descs = (start, traili, trail2, trail3, trail4,
trailS, trailé, coll, col2, col3, col4,
rubblel, rubble2, mound, monastery,
cellar, cave, slopel, slope2, slopes,
slope4, slopeS, ruti, rut2, ruts,
steepl, steep2, steep, steep4, steeps,
peaki, peak2, peakl, peak4, peaks,
234
saddlel,
mtcavelb,
mtcave2c,
trolls,
nowhere.
wmmbl ab «
WMMS PS x
tgablure,
trfollow,
goodies =
fakes
trash
wmamgoady
collection
VAR
lacatioans
trollocs:
wmmwant ss
wonmhass
womf AKeSs &
stash:
whatshere:
visited:
ob jname:
tLurnss
troltime:
wmmcount s
trlives:
saidwaddas
walkstick,
saddle2, saddle,
mtcavelc, mtcave2Za, mtcaveczb,
gullyl, gully, hilll,
ridgel, ridge2, wmm, chasm,
cellardown, fountain, trtalk,
womharp, wmmhello, wmmspl,
woamsp3, wmmsp4, wmmsps,
tgabbones, trhungrys,
helpspiel);
mtcavela,
“helpspiel” doesn’t exist in the description database
on page 303, see pages 224 and 303.
start. .nowheres
(nuggets, Silver, diamonds, beryl,
amethyst, carvings, wine, wineskins,
flowers, axe, hammer, flint, keys,
antlers, talons, feathers, eggs,
leather, cymbals, drum,
bones, scroll, wheel, ramshoarn,
berries, knife, burner, incense,
tooth, noaabj)s
Hi
i
nuggets..wineskings
flowers. .bones;
scroll..tooth;
SET OF goodies:
rooms:
SET OF rooms;
collections;
collections;
collectian;
collectian;
ARRAYE roams] OF callections;
ARRAYCrooms] OF INTEGER:
ARRAY Cgoadies] OF STRINGLISI;
INTEGERS
INTEGER s
INTEGERS:
BOOLEAN:
{true when trolls still around?
BOOLEAN:
235
isopen: BOOLEAN:
eaten: BOOLEAN:
killeds BOOLEAN:
dane: BOOLEAN;
FUNCTION rand (low, high: INTEGER) : INTEGER;
IMPLEMENTATION
VAR
re rooms:
{ loop control for initialization }
FUNCTION rands
VAR
mx. Cy Gs INTEGER;
BEGIN
rand := O;
IF low = high
THEN
rand == low
= high ~ low + 1s
= (maxint ~ high + low) DIV c + 13
= mx *& (high ~- low) + (mx - 1);
REFEAT
d := randam
UNTIL d «= mxy
rand := low + d MOD cy};
END € IF low = high 33
END { FUNCTION rand 33
FROCEDURE initds;
BEGIN
location := starts
ob jnamelCnuggets] r= "nuggets" s
ob jnamelsilver] = “silver’s
236
"diamonds";
*beryl* 5
"amethyst’ 3
*"carvings’ j
"wine";
*7wineskins’ 4
*flowers’ 4
ob jnamelCdiamonds]
ob jnameCberyl J
ob jnamelamethyst ]
ob jname[carvings4
ob jnamelCwined
ob jnamelCwineskins4
ob jnamel flowers)
Hou HOW OH ROD OH
ob jnamelLaxed 7 axes
ob jnameChammer J "hammer ” ;
ob jnamelflintd Flint’;
ob jnamelkeysd "keys":
Zantlers’ ;
*taloans’:
*"feathers’ 3
"eagle-eqgs’ ;
*walking-stick’ 5
*leather’;
*cymbals’” ;
> drum’ s
*bones’* 5
*scroll’s
*prayer-wheel *;
7 ramshorn’ §
*berries’ ;
*knife’s
*incense-burner’ ;
*incense’;
*tooth* s
ob jnamelantlersd
ob jnameCtalons)
ob jnamelfeathers)
ob jnameleggs4
ob jnamelCwalkstick]
ob jnamelCleather J
ob jnamelCcymbals]
ob jnamelCdrumd
ob jnameCbonesd
ob jnameCscroll 3)
ob jnamelwheel J
ob jnameCramshornd
ob jnamelCberries]
ob jnamelCknifed
ob jnameCburner I
ob jnamelincensel
ob jnamelCtooth)
Huu ft t tf t bo ot ft bh hho oR
FOR r s= start TO chasm DO
BEGIN
whatsherelCr] :=
visited(€rd r=
END ¢€ FOR r s= start ««. 33
END { FROCEDURE initi 33
ae . “diamonds” are never assigned to a location here. The game works fine
PROCEDURE inites without them, but you could assign them to any location on the map, or
BEGIN even better, write a routine which places them in a random location.
whatsherel(peaki J scroll ds
2= OE
whatshereCpeak2] := Cramshornd;
whatsherelpeaks] := CLincensel;
237
whatsherelpeak4]
whatsherelpeakSd
ow tou
Cburner ds
Cwheel J;
whatshereltrail 3] : Cwalkstick, axel;
whatsherelcave] : Cbhonesd;
whatsherelCmound] := Cflowersds;
whatsherel(mtcavelbd := Cknifed;
whatsherelCruts] r= Cnuggetsd;
whatsherelrubble2d := Csilverd;
whatshereltrolls] := Lamethyst,
WINE.
wineskind;
whatshereCgullyld := Cheryl;
whatsherelridge2 := Ccarvings,
keySs
berriesd;
whatsherel(mtcavezal r= Cflintd;
whatshereltrailé] := CLantlersd;
whatsherel(steepS] 2:= ECtalonsd;
whatsherelsaddle2d := Cleatherd;
whatsherelsteep4] r= CLegqgs 1s
whatsherelCmonastery] := Cdrum, cymbalsi3
whatsherelcellard] := Chammer;
whatsherelsteep3] := Cfeathersd;
trollocs := (Ctrolls, slope2, steep2, peak=,
saddlel, coli, saddle2,
mtcavela, mtcavelb, traild,
trail2, rutl, ridge2d;
wmmwants := Cscrall..toothd;
woamhas := C5
womfakes := Cnuggets..wineskinsd;
stash s= C3
turns 2 Of
troltime := O;
wmmcount = OF
trlives r= trues
saidwadda := false;
isopen := false;
eaten := false;
dane := false;
END { FROCEDURE init2 33
REG IN
initl,;
initis
END.
238
Larger Programs: Using UCSD Units
As your adventure game programs grow larger and
larger, it will become more and more difficult to
work with them. Even with the include option of the
Pascal compiler, the program itself is still one
large, monolithic piece of code. Using lots of proce-
dures and functions helps to organize it, but it would
be convenient to break it into pieces. The UCSD
Pascal extension known as a unit is a way to break
large programs into smaller ones. In this chapter I
shall discuss UCSD units and show how to use them
in adventure games.
WHY UNITS?
You may question the usefulness of units. As
with other techniques, there are both advantages
and disadvantages to using units in writing Pascal
programs. I will emphasize the positive and discuss
the advantages.
m@ Each UCSD Pascal unit is separately compiled.
This can mean shorter compilation times if you
have enough units: instead of having to recompile
the entire program, you only have to recompile
one unit. This is not the whole story, as you shall
see later on. In general, however, units, when
properly used, can save you time.
m@ Each unit is a self-contained piece of the larger
program. You can treat it like a small program
itself. This reduces the amount of code you have
to deal with at a single time and simplifies your
design and programming effort. It is possible to
misuse units and throw away this advantage.
However, if you keep logically related code to-
gether in a single unit or in several related units,
you will simplify your programs.
@ Units can be used to “hide” certain information
from the programs that use the units. This is a
subtle concept that takes awhile to absorb. I shall
try to elucidate further as I elaborate on units and
their use.
THE UNITS IN ADVENTURE 3
The following pages describe the units used in
Adventure 3 and how they interact with each other.
239
The Main Program
Every Pascal program must have a main pro-
gram, whether it uses units or not. The main pro-
gram is not itself a unit, but it must contain a uses
statement that requests all the units that the pro-
gram requires. I explain the uses statement when I
go into the details of unit syntax and semantics.
There are seven units used in Adventure 3, one of
which comes from the SYSTEM.LIBRARY. The
remaining six units are part of the code of Adven-
ture 3 itself.
The Locations Unit
Units are arranged in a hierarchical fashion, as
noted in the diagram in Chapter 25. The top level
units contain uses statements for the lower level
units that they rely upon. The locations unit is the
highest level unit in the Adventure 3 hierarchy. It
contains the locations procedures pwmm and
pcellar. Any other location procedures that might
be needed in further developing Adventure 3 should
be placed into the locations unit.
The cmds1 Unit
The cmds1 unit contains the general support
procedures for command processing: cmdlookup,
listen, docommand, whichway, noway, and
travel. In addition, it contains three of the specific
command procedures: pcarry, pdrop, and pshout.
Finally, it contains the code for dealing with the
problem surrounding the trolls: ckproblems,
trolmeal and trolaction.
If a procedure or function needs to call the
solved function, which is in the probs unit, it must
be included in cmds1. Otherwise, it may be placed
in one of the other cmds units.
The cmds2 Unit
This is a very small unit—it contains only the
phelp procedure. The procedures that go into the
cmds2 unit are those specific command procedures
that do not need access to any data declared in the
advdata unit. Because most commands do need ac-
cess to some sort of data, very few procedures will
wind up here. Commands that could be added,
240
whose implementation procedures could be placed
in cmds2, are those that cause no change to the
status of the adventure; instead they cause some
sort of reaction from the guide in the form of
dialogue. The help command is a good example.
The only side effect of the use of the help command
is an increase in the count of how many times help
has been requested. This total is used in the scoring
at the end of the game.
The cmds3 Unit
The cmds3 unit contains two other command
implementation procedures; pinventory, and
plook. It also contains procedures used in display-
ing descriptions of locations and objects: show,
showgoodies, objlookup, and ckgoodies. None of
the functions or procedures in cmds3 need access to
the probs unit. That is the general rule for putting
them in cmds3 as opposed to cmds1. They do need
access to various pieces of adventure game data,
however, and that is the criterion used to decide
that a procedure should go into cmds3 instead of
cmds2.
The probs Unit
I use the abbreviation for the probs unit, be-
cause the full name, problems, is used inside the
unit for the name of an enumerated type. Probs
contains a single function called solved, which
evaluates whether various problems in the adven-
ture have been solved or not. It is not the sole
arbiter of problem solution. However, it is a conve-
nient way to centralize and organize the approach to
parts of problems that depend on complicated
Boolean expressions.
The advdata Unit
This unit contains the bulk of the types and
variables needed in more than one place elsewhere
in the adventure. It is at the bottom of the uses
hierarchy so that all those units above it can “see”
the data that it declares.
UNITS: SYNTAX AND SEMANTICS
A unit is a self-contained UCSD Pascal compi-
lation entity. That rather pompous statement sim-
ply means that you can compile a unit by itself.
Several units can be compiled separately as part ofa
single larger program. When you compile a unit, a
code file is produced, just as when you compile a
program. The difference is that the resulting code
file may not be X(ecuted. It must be linked with
other code files, including at least one from a pro-
gram that uses the unit. There is a relationship
between the code files for different units of a single
program. It mirrors the relationship dictated by the
uses statements in the various units and program
itself. I discuss these relationships further when I
delve into uses in detail below.
Every unit has three parts: a unit heading, an
interface part, and an implementation part. The
interface part declares what in the unit is public,
that is, available to those units and programs that
use the unit. The implementation part implements
the functions and procedures declared in the inter-
face part and may contain other Pascal code whose
scope is limited strictly to the implementation part.
The Unit Heading
The unit heading is analogous to the program
heading. It appears at the start of the unit and gives
a name to the unit. The syntax is trivial:
UNIT unitname;
The only other requirement for units is that the
compiler swapping option must be turned on before
the unit heading:
{$S+}
The Interface Part of a Unit
The interface part of a unit is initiated by the
key word INTERFACE. The interface part there-
fore may contain CONST, TYPE, and VAR decla-
rations as does a normal Pascal program. All con-
stants, types, and variables declared in the inter-
face part behave as if they were declared in the main
program’s outermost part. This is another way of
saying that these declarations are public. People
also describe the situation by saying that the inter-
face declarations are exported to the rest of the
program (actually to any program or unit that uses
the unit in question).
After the CONST, TYPE, and VAR parts of the
interface, there may be declarations of procedures
and functions. That is, the procedures and functions
whose code will appear in the implementation part
have their headings in the interface part. The idea
here is that the interface part tells you what proce-
dures and functions are available in the unit and how
to call them. It does not reveal any details of how
the procedures and functions are actually im-
plemented. This allows the implementation to
change without requiring the users to recompile
their own code. This may seem trivial on first
glance, but it is actually one of the important ideas
in modern programming language design.
The interface part of the unit ends with the last
procedure or function declaration. The interface
may contain no code. It must declare at least one
procedure or function; that is, you cannot have a
unit that contains only data.
The Implementation Part of a Unit
The implementation part of a unit is started
with the key word IMPLEMENTATION. It is the
private part of the unit and may be changed without
requiring the users of the unit to recompile their
code. This statement is only true if no change is
made to the interface part. The following things that
cannot change are
@ The specific constants, types, and variables de-
clared cannot change. Any additions or subtrac-
tions here will cause a need for recompilation.
@ The headings of functions and procedures cannot
change: the number and types of parameters and
the return types of functions must be untouched.
The implementation part of a unit may have its
own CONST, TYPE, and VAR declarations. Any-
thing declared there is strictly private to the unit
and cannot be referred to anywhere else in any
program that uses the unit. The implementation
part must contain code for all the functions and
procedures declared in the interface part. It may
also contain its own private procedures and func-
241
tions (which may be called from within the public
procedures and functions inside the unit, but may
not be called from outside the unit). The code for
the implementation of the interface procedures and
functions must not repeat the entire heading of the
procedure or function. Only the key word PROCE-
DURE or the key word FUNCTION must appear,
followed by the name used in the interface declara-
tion. For example, in the probs unit of Adventure 3
you find the declaration
FUNCTION solved (which: problems) :
BOOLEAN;
In the implementation part of probs, the code for
solved begins with
FUNCTION solved;
Any repetition of the heading other than the parts
indicated will cause a syntax error to be generated
by the compiler.
The Uses Statements
Units are used by other units and by Pascal
programs. The UCSD Pascal language provides the
uses statement for telling the compiler which units
are being used. Uses statements immediately fol-
low the program heading in Pascal main programs.
Units may also use other units and the uses state-
ments in units immediately follows the INTER-
FACE key word.
Each unit that is used in a program or other unit
must be located by the compiler in a library code
file. I shall not go into all the details of li-
braries in UCSD Pascal. The compiler is nice
enough to provide a way to avoid the hassle of
having to install units in libraries before using
them. The compiler option U may be used to specify
a code file to search for a unit at compile time. This
is best illustrated with examples. In the main pro-
gram of Adventure 3 you see
PROGRAM mtadvent;
{$S+}
USES applestuff,
242
advdata,
probs,
cmds3,
cmds2,
cmds1,
locs;
$Umt1 .code}
$Uprobs.code}
$Ucmds3.code
$Ucmds2.code
$Ucmds1.code
$uloc.code}
The first item to note is that the uses statement
allows a list of units to be specified. The total
number of units allowed is limited by the number of
Pascal segments available. This may vary from
system to system. I never exceed it, so I say no
more. You should be aware that there is a limit.
In the above example, each unit except the
first has a {$U . .. compiler option preceding the
name ofthe unit. If no {$U ... appears, the compiler
will search SYSTEM.LIBRARY for the unit. In
Apple Pascal, SYSTEM.LIBRARY happens to
contain a unit called applestuff. This unit provides
the random function used in the advdata unit of
Adventure 3. If you enter Adventure 3 in your
system, you should replace the reference to
applestuff to the equivalent unit (or provide ran-
dom in some other way).
When a unit in the uses statement list is pre-
ceded by a {$U . .. option, the file name specified
there is used by the compiler. This file must be a
code file and must contain the code generated by the
Pascal compiler when that unit was compiled. For
example, mtl.code must contain the code for the
advdata unit. If no such file exists, or if the file does
not contain code, the compiler will issue an appro-
priate error message and will no doubt have great
difficulty continuing.
You can now see that if a unit is to be compiled,
all the units that it uses must be compiled first. This
means that the uses statements in all the units of a
program cannot contain any circularities. For
example if Unit A said USES B, Unit B said USES
C, and Unit C said USES A, you could never com-
pile A! Why? Because in order to compile A you
would first have to compile B because A uses B. In
order to compile B, you would first have to compile
C because B uses C. But in order to compile C, you
would first have to compile A, because C uses A!
Therefore, in order to compile A, you would first
have to compile A—but that is impossible: you can’t
compile A before you compile A.
In Adventure 3, mtl.text must be compiled
first to provide a code file containing the advdata
unit. Then cmds3 and probs can be compiled in any
order (they both use advdata). cmds2 can be com-
piled at any time because it does not use any other
unit, but it must be compiled before cmds1, which
uses it. cmdsl may be compiled after advdata,
probs, and cmds2 are available. locs is the last unit
of Adventure 3 to be compiled: it uses all the
others. Finally, the program of Adventure 3 cannot
be compiled until all the units of Adventure 3 (in-
cluding locs) have been compiled.
Compile in this order, followed by mtadvent
LINKING PROGRAMS THAT HAVE UNITS
When you compile the main program of Ad-
venture 3, the resulting code file may not be
X(ecuted. If you try, the system will tell you:
Must L(ink first
The process of linking combines the separately
compiled main program code file with all the code
files for the units that it uses. The result is another
code file, which is X(ecutable. In the UCSD sys-
tem, linking is accomplished by using the L(ink
command. This requires that the SYSTEM
.LINKER program be on line.
Using the UCSD linker is relatively simple. It
will engage you in a dialogue. It wants to know the
names ofall the files it needs in order to perform the
linking process. It starts out by requesting
Messages are more user-friendly in Pascal 1.3
HOST FILE?
This file will always be the code file of the main
program for our adventure games. Thus, respond
with Specify drive volume or disk name if needed:
“#5:mtadvent” for Drive 2 or
HOST FILE? mtadvent “dsknm:mtadvent”
assuming that the main program has been compiled
into mtadvent.code.
The linker continues by asking:
LIB FILE?
The response here should be the name of a .code
file containing one of your units. The question will
be repeated and you should respond by giving the
names of all the .code files for the units required by
the program:
LIB FILE? mt1 Specify drive volume
LIB FILE? probs or disk name if needed:
LIB FILE? cmds3 “#5:mt1” for Drive 2
LIB FILE? cmds2 or “diskname:mt1”
LIB FILE? cmds1
LIB FILE? locs (See note on page 3 of this
LIB FILE? PDF for more information)
When you are finished, simply pressing RETURN
will cause the linker to stop prompting for lib files.
It then requests:
MAP FILE?
It is safe to answer this by again pressing RE-
TURN. The map file is a file of technical informa-
tion generated by the linker. You won't need this
file in the ordinary course of linking adventure
games.
At this point in the dialogue, the linker will
read in all the code for the program being linked and
finally will ask:
OUTPUT FILE?
For Adventure 3, respond with Again, specify
volume # or
OUTPUT FILE? advent = diskname
as needed
The linked program will be placed in the ad-
vent.code file. You may then execute the adventure
game by commanding the system to X(ecute and
answering with advent when it asks Execute what
program?
MAINTAINING A
PROGRAM WRITTEN WITH UNITS
One of the advantages of using units is that you
have less compiling to do than with large,
monolithic programs. Whenever you change a unit,
243
you do not necessarily have to recompile all the
units in the entire program. The general rules are
as follows:
@ If you make a change in a unit that does not
involve changing the interface part of the unit, all
you need to do to incorporate the change is
1. Recompile the unit
2. Relink the program
@ If you change the interface to a unit, you must
recompile all other parts of the program that use
the unit. In order to know which parts are in-
volved, it is good to keep a diagram of the uses
relations between all the units in your program. I
showed such a diagram for Adventure 3 in
Chapter 25, Fig. 25-7.
Starting from the top of the uses diagram, work
downwards until you find the unit whose interface
has changed. Then recompile that unit and all the
units (including the main program) above that unit.
244
Then relink the program in order to incorporate the
change. If you are careful, this rule is not difficult to
follow. On the other hand, if you don’t keep a good
uses diagram and are careless about recompiling all
the units that can “see” a change to an interface,
your program will start behaving very strangely.
When you have “weird” bugs that you simply do not
understand, one good idea is to just recompile ev-
erything.
To illustrate the above rule, consider the
probs unit of Adventure 3. If the interface to probs
is changed, all units above probs must be recom-
piled. This consists of the units probs itself, cmds1,
locs, and the main program.
A unit is considered to be above another unit if
there is an arrow or sequence of arrows in the uses
diagram leading from the other unit to the unit in
question. For example, if we start at locs, there is
an arrow pointing directly to probs. Thus, locs is
above probs. Starting at main, we can reach any
other unit by a sequence or arrows. Thus, main is
above all units in the program. In other words, main
must always be recompiled if the interface to any
unit changes.
Problems in Adventure 3
The problems in Adventure 3 are more complex
than those in earlier adventures. They pose their
own special implementation difficulties. In this
chapter I dwell on the techniques used to imple-
ment the problems in Adventure 3.
THE PROBS UNIT
In the introduction to Adventure 3 and again in
the last chapter, I touched on the probs unit. The
probs unit exemplifies a new approach to problems
in general. The solved function in the probs unit is
used to centralize the evaluation of complicated
Boolean expressions that are part of the problem-
solving process. This makes the representation of
problems in general both simpler and more sys-
tematic.
Solved is a Boolean function, that is, it returns
either true or false. It takes a single parameter,
which of type problems. The enumerated type
problems= (wmmhappy, luretrolls,
bonetrolls, canopen, ftofyouth);
is declared in the interface of the probs unit. Its
values represent the individual Boolean expres-
sions needed for the problems in Adventure 3. The
units that use the probs unit can “see” the declara-
tion of problems and can pass those values to
solved.
The solved function consists of a simple case
statement controlled by the parameter which:
CASE which OF
wmmhappy: solved :=
(wmmhas= wmmwants) AND
- etc.
solved simplifies the approach to problem rep-
resentation. Take for example the problems value
luretrolls and its appearance in solved:
luretrolls: (troltime > 0) AND
((turns — troltime) > 0) AND
((turns — troltime) < 25);
The complicated expression for luretrolls is
245
evaluated once and for all in solved. The rest of the m The first time you see them is the first time you
code for the adventure can use statements like
IF solved (luretrolls)
THEN
or
IF NOT solved (luretrolls)
THEN
which are simple and easy to understand. The ad-
venture game writer may think in terms of solved
rather than in terms of the more complicated ex-
pressions hidden inside solved.
Hiding complicated problem expressions in-
side solved can be a debugging aid as well. Suppose
there are many different references to a given
Boolean expression (like the one corresponding to
luretrolis). Suppose the formulation of the expres-
sion was incorrect. With solved you only have to fix
one place in the program. The calls to solved don’t
need to change. Without solved, you would have to
fix all the places where the expression is used,
making sure that the expressions in all those places
were identical. Once you found all the places and
edited in the change, you would have to recompile
all the units or programs involved. With solved you
only have to recompile the probs unit (assuming
that the fix did not change the interface to the unit).
In the course of debugging a long and complicated
adventure, this simplification can be a great advan-
tage.
THE PROBLEMS IN ADVENTURE 3
This part of the chapter describes the various
problems that must be solved in order to success-
fully complete Adventure 3.
The Trolls
Adventure 3 presents the trolls, a hairy, hun-
gry bunch of monsters who will eat you if you are
not careful. The trolls start out at the trolls location
(where else?) and there are several facets to their
behavior in the adventure:
246
visit the trolls location.
1. If you arrive at the trolls location empty
handed, you will be promptly dispatched.
In short, the trolls will eat you!
2. If you are carrying the bones with you
when you arrive at trolls the first time, the
trolls will follow you. You may thus lure
the trolls away from their mountain lair.
This turns out to be the only way to get
past their location.
@ The trolls will follow you (as long as you are
carrying the bones) to any of the locations in the
collection trollocs:
trolls, slope2, steep2, peak2, saddle1,
col1, saddle2, mtcave1a, mtcave1b,
trail5, rut1, ridge2 Also trail2, but oddly
not mtcavelc
If you drop the bones at any time before solving
the trolls problem and subsequently visit one of
these locations, the trolls will materialize there
and promptly eat you.
@ The trolls may be pacified by luring them to
mtcave1a and dropping the bones there.
w If you do succeed in pacifying the trolls, their troll
teeth will forever after be located in mtcave1a.
You can pick them up and carry them elsewhere,
drop them, return to mtcave1a and there will be
another tooth there! The trolls break their teeth
crunching away on the bones you drop in
mtcave1a and their teeth then litter the floor of
said cave.
@ Once the trolls have been pacified, they no longer
bother you. They are removed from the adven-
ture forever after.
Finding the Bones
The bones are initially located in the cave
beneath the cellar beneath the monastery. In order
to get into the cave, a hatch must be opened. This
requires that you have the keys with you. You must
find the keys (located elsewhere in the adventure)
and return to the cellar in order to get into the cave.
Of course, just finding the bones is not enough.
You must also carry them with you back to the trolls
location in order to solve the trolls problem. There
are various subtle hints to this effect in the descrip-
tions that accompany being eaten by the trolls when
you fail to have the bones. After a few failures, you
will figure out what to do.
Making the Wise Man of the Mountain Happy
Strewn about the map of Adventure 3 are many
seeming treasures. These treasures are fakes as far
as the wise man of the mountain is concerned. He is
more interested in “spiritual” things. If you bring
the fakes to him and drop them at the wmm loca-
tion, you eventually get reprimanded for your ef-
forts. The problem of making the wise man of the
mountain happy involves:
@ Realizing that the conventional treasures are of
no interest to the wmm.
@ Figuring out which items in the adventure will
make the wmm happy.
@ Bringing all the wmm treasures to the wmm
location and dropping them there.
There is a subtle way to figure out which items
the wmm values. If you bring something to the
wmm location that the wmm considers to be of true
value and drop it there, it disappears completely. In
other words if you say drop x and then try to carry x,
you will get “I don’t see any x here” in reply. This
will seem strange at first, but the idea is that the
wmm is immediately and silently spiriting those
items away to his own private collection.
Opening the Doorway to the Fountain of Youth
There is a secret word that the wise man of the
mountain will reveal after you have brought him all
his true treasures. This word, if uttered in the
cellar beneath the monastery, has the effect of
opening the door to the fountain of youth. If you then
go down you will win the game.
IMPLEMENTING ADVENTURE 3 PROBLEMS
I now delve into the Pascal details of Adven-
ture 3 problems. I shall occasionally use the
precondition—postcondition terminology of
Chapter 15. If you skipped that chapter earlier, now
would be a good time to read it.
The Trolls: First Encounter
The first implementation headache involves
the trolls. The problem is how do you prevent the
trolls from “following” the player before the first
encounter with them? If you look at the map of
Adventure 3, you will see that it is impossible to
reach the trolls location without first passing
through at least two or three other locations in the
trollocs set. The solution to this implementation
problem is implicit in the use of the variable trol-
time.
The variable troltime is an integer. It is ini-
tialized to 0 in the initialization part of the advdata
unit. As long as troltime remains equal to 0, the
trolls can only appear at the trolls location. The
trolocation procedure, invoked whenever the value
of location is one of those in trollocs and the trolls
are still “alive” handles this:
IF (troltime = 0) AND (location = trolls)
THEN
The trolocation procedure is responsible for
more than just the first encounter with the trolls.
The other code in trolocation must be written so
that it will not be invoked before the first encoun-
ter. The rest of the code consists of two IF state-
ments. In both of these statements the condition
solved (luretrolls)
must be met before the statements will be exe-
cuted. In turn, the Boolean expression for the lure-
trolls problem contains the condition
troltime > 0
All in all, troltime guards the first encounter and
forces it to occur at location = trolls.
What Happens at Trolls the First Time?
247
The trolls problem as described earlier requires
that the player be carrying the bones in order to
avoid being eaten. This is easy to express in Pascal:
IF bones IN stash
THEN
BEGIN
troltime := turns; {solve luretrolls
problem}
show (trhungry);
END
ELSE
trolmeal
{END IF bones IN stash};
If the player is carrying the bones:
bones IN stash
the luretrolls problem is solved (for the time being)
by setting troltime equal to the value of turns. Of
course, by now the value of turns must be > 0. Ifthe
player should be unlucky enough not to have the
bones, a procedure called trolmeal is invoked.
Needless to say, the trolls’ meal is not potatoes and
gravy!
Here are the preconditions and postconditions
for this problem:
Preconditions:
(troltime = 0) AND (location = trolls)
bones IN stash
Postcondition
troltime > 0
Of course, if the preconditions are only partially
met, the trolls eat the player and the game is ended.
The Trolls: After the First Encounter
The problem expression for luretrolls has the
following component:
(turns - troltime <= 25)
248
This means that the player must get rid of the trolls
within 25 turns after first luring them. This is plenty
of time, once you know how. It may take a few
games (and troll meals) to learn how to get rid of the
trolls. After all, the trolls have to have some fun
too. t There’s no code for this, and a big bug! See page 212
Getting Rid of the Trolls. In order to get rid
of the trolls for good, the player must
1. Lure the trolls to location mtcave1a.
2. Drop the bones and scram.
The trolls will remain in mtcave1a and mer-
rily feast on the bones left behind. Then they will
melt away into the mountains, never to appear
again. The implementation problem this poses is
how to detect the successful completion of 1 and 2
above.In particular, how do you detect when the
player has left the cave after dropping the bones
there in the presence of the trolls?
There is a special procedure in Adventure 3,
called ckproblems. It is silently invoked after
every command. It is called in order to see if the
execution of acommand has caused any problems to
be solved. The current implementation of Adven-
ture 3 caters to two situations:
1. The player has successfully lured the trolls to
mtcave1a and dropped the bones. This situa-
tion may be detected while the player is still in
location mtcave1a.
2. The trolls have been disposed of and the player
has left mtcave1a. It is at this juncture that the
tooth is made to materialize in the cave: part of
the problem solution is to return to the cave and
find the tooth. This takes courage on the player’s
part because he knows the trolls were there
when he last visited the cave. The wmm must be
given the tooth (among other objects) in order
for the player to win the game.
The first situation is detected as follows:
Precondition
solved (luretrolls) AND solved (bonetrolls)
Postcondition
trlives = FALSE
NOT (bones IN whatshere[mtcave1a])
The second situation is detected as follows:
Precondition
(NOT trlives) AND (location <> mtcave1a)
Postcondition
tooth IN whatshere[mtcave1 a]
The first clause of the second situation is only made
true by successfully achieving the first situation as
you can see in the postconditions for the first situa-
tion.
The Wise Man of the Mountain
The wise man of the mountain sits on the
grandest peak in the mountain range and dispenses
“wisdom.” The player must bring the wmm (ab-
breviation for wise man of the mountain) the
goodies that he deems valuable. The wmm goodies
are not the conventional treasures like gold and
silver. Part of the challenge to the player is to
determine what objects the wmm might desire.
There is one obvious hint that the player re-
ceives: every time the player drops one of the wmm
goodies at location wmm, that object will disap-
pear. In other words, if the player says:
drop incense
and then
carry incense
the response will be:
| don’t see any incense here.
The object goes directly into the collection owned
by the wmm.
In addition the player must remove everything
else from the wmm’s sight in order to make him
happy. That is, whatshere[wmm] must be the
empty set of objects. This may be the most difficult
part of the problem to solve. The wmm gives only a
vague hint. In the descriptions database, the de-
scription corresponding to $wmmharp contains an
exhortation to take certain items from the wmm’s
sight.
The Secret Word
When the wmm has all the objects he desires
and the wmm location is bare of all other trash, the
wmm will reveal the “secret word.” The word is
wadda. The player must return to the cellar and
then say
wadda
in order to open the hatch leading to the fountain of
youth. Once that has been accomplished, going
down from the cellar brings you to the fountain and
wins the game. If you try to say “wadda” before you
have made the wmm happy, your guide will refuse
to understand. This is controlled by a Boolean vari-
able saidwadda. It may only be set to true when the
canopen problem has been solved.
Preconditions
(wmmhas= wmmwants) AND
(whatshere[wmm]= [ ])
(location = cellar)
Command given is: “wadda”
Postcondition
canopen = TRUE
249
Other Techniques Used in Adventure 3
As in the other adventures I have presented, there
are a number of ad hoc Pascal coding techniques
used in Adventure 3. This chapter is devoted to
explaining why they are there and how they work.
PUTTING MORE INTO THE DATABASE
The descriptions database for Adventure 3
contains more than just descriptions of locations.
The enumerated type rooms of Adventure 2 has
become the type descs in Adventure 3. The iden-
tifiers in the declaration of descs fall into two se-
quences: those before nowhere and those after
nowhere. The identifiers before nowhere comprise
the locations for Adventure 3. There is a subrange
declaration
rooms = start . . nowhere;
that indicates this. The identifiers that follow
nowhere all correspond to special descriptions
used in Adventure 3.
250
Various Descriptions Involving the Trolls
The trolls play an important part in Adventure
3. They and their actions are described in great
detail as the game progresses. Rather than embed
this description in the game code itself, it has been
put into the database. The following “placenames”
correspond to troll descriptions:
@ trhungry—When you arrive at the trolls’ location
carrying the bones, you get this message. It tells
you that the trolls exist and that they are eager to
eat you. It also boldly hints that the bones are
connected with their interest. You will know this
for sure if you played the game before and man-
aged to get eaten.
@ trfollow—This is what the player is told when the
trolls are following him or her. It describes the
trolls and hints at the solution of the trolls pro-
blem by continuing to mention their interest in
bones.
@ trtalk—The player gets only one chance to read
this message! It describes the player’s demise
upon reaching the trolls’ location without carry-
ing the bones.
@ tgablure—When the player lures the trolls to
mtcaveta, he or she will still be carrying the
bones. This is the big fat hint to drop them there.
@ tgabbones—If the player succeeds in luring the
trolls to mtcave1 a and is smart enough to drop
the bones there, he or she is rewarded with this
message. It describes the trolls sudden switch in
interest from you to the bones you have dropped.
It also hints at the tooth problem by mentioning
breaking teeth. The player should connect this
hint to the wmm hint later about troll’s teeth.
Descriptions Involving the
Wise Man of the Mountain (wmm)
There are many entries in the Adventure 3
database that involve the wmm. Most of them are
things that the wmm may say to the player at some
point during the play of the game. Here is a sum-
mary of the wmm related entries:
@ wmmsp1, wmmsp2, wmmsp3, wmmsp4,
wmmsp5—These are five “spiels” of varying
length that the wmm gives the player on second
and subsequent visits to his domain. They con-
tain hints, varying in their degree of obvious-
ness, about the objects that the wmm truly de-
Sires as treasures.
@ wmmhello—This is the initial speech delivered
by the wmm when the player first visits the wmm
location. It is a general description of the fact that
the player should find various treasures and de-
liver them to the wmm. It deliberately misleads
the player (or tries to) into thinking that the
treasures desired are of the conventional variety.
@ wmmharp—This speech is delivered as soon as
the player manages to bring enough bogus trea-
sures to the wmm. The number of fakes de-
livered so far is recorded in the variable
wmmcount. How wmmcount is kept up to date
is discussed later in this chapter. If wmmcount is
greater than or equal to three, this message is
displayed. It gives the player the rude surprise of
finding out that what the wmm means by trea-
sures is not the usual meaning. It also contains a
hint about the trolls tooth.
@ wmmblab—When the player succeeds in
gathering all the wmm’s true treasures and de-
livering them to location wmm, the wmm spills
his secret. The location of the fountain of youth
is dispensed along with a magic word for opening
the doorway to that fountain.
The Fountain Description
The desc identifier fountain corresponds to
the description of the fountain of youth. This is
displayed only at the very end of the game when the
player has succeeded in opening the hatch leading
to the fountain. It could have been made an ordinary
location, but because the game ends as soon as the
player arrives, it was easier to handle it as a special
description.
Modifications to the Show Procedure
The addition of descriptions not corresponding
to adventure game locations could have been ac-
complished in another way. I could have created a
separate database. The disadvantage of this is the
requirement for two more open files during the play
of the adventure game. Each file opened while the
UCSD Pascal program is running causes a perma-
nent requirement for RAM memory. The more
RAM used for file, the less RAM there is available
for coding features into the adventure. Therefore, I
decided to extend rooms to descs as I have already
begun to explain. This causes the show procedure
to change, as I shall now discuss.
The show procedure simply prints lines from
the descriptions database on the player’s screen. In
Adventure 2, these descriptions were limited to
adventure game locations. In Adventure 3 they can
include descriptions of troll activity and speeches
given by the wmm. Recall from Chapter 16 that
show only displays the description of a location
when the variable chgloc is true. During the de-
velopment of Adventure 3 situations arose when
chgloc was still false, yet a description other than a
location description was needed by the Pascal code.
This required that show be changed in order to
force out nonlocation descriptions when chgloc was
false.
251
The solution involves the use of the intrinsic
function ord. The ord function may be used to com-
pare the position of two identifiers in the declara-
tion of an enumerated type. If one identifier appears
after another in the declaration, the ord value for
the second identifier will be greater than that for the
first identifier. For the descs enumerated type, all
identifiers corresponding to special descriptions
come after the identifier nowhere. The ord func-
tion can detect this as follows:
ord (where > ord (nowhere)
This is one of the additions to the tests used in
show to determine when to print a description and
when not to print it.
An unexpected side effect of nonlocation de-
scriptions also turned up in Adventure 3. Part of the
act of showing a location to the player is showing all
the objects that happen to be there. In Pascal terms,
the procedure showgoodies, which describes the
objects present, is invoked from inside the proce-
dure show, which describes the unchanging physi-
cal appearance of the location. When nonlocation
descriptions were added, the listing of objects
present in certain locations started to be repeated.
Whenever there were objects present in a location
and a nonlocation description was printed while the
player visited that location, the objects there were
described twice. For example, the first time at
location trolls, the trolls location description was
given; then the objects present were listed; then
the trtalk message was printed; then the objects
present were listed a second time.
The solution to the multiple showing of objects
at a location also involved the use of ord. The idea
is—never call showgoodies for a nonlocation de-
scription. This is accomplished by placing the call
to showgoodies inside an IF test that uses ord:
IF ord (where) < ord (nowhere)
THEN
showgoodies;
A Few Pitfalls to Watch Out For
In the process of making rooms into descs
252
some problem areas were noted. Here isa brief list,
so you can watch out for them when you start writ-
ing your own adventure games.
Value Out of Range Errors. There still
exists a rooms type in Adventure 3. It is declared as
a subrange of descs:
rooms = start . . nowhere;
If you declare variables of type rooms—for exam-
ple, loop control variables for loops that range over
the values in rooms—you must be careful not to
assign those variables values that are “out of
range.” For example
VAR
r: rooms;
BEGIN
FOR r := start TO fountain DO
Because ord (fountain) > ord (nowhere), fountain
is not a legitimate value of type rooms. The Pascal
compiler unfortunately does not check this in the for
statement. The check does occur when the program
runs, however. The assignment r := fountain
causes the program to come to a screeching halt.
The moral of the story is—Be careful!
MAKEDESC Considerations. The source
for the adventure game database must be handled
carefully. The main requirement is that there must
be a placename description line for every identifier
in the descs enumerated type. Even if an identifier
has no description actually associated with it, there
must be a placename description line for it. In par-
ticular, nowhere must have its own placename de-
scription line. If this rule is not followed, the index
to the database may get out of whack. That is, the
index entry for a given identifier may point to the
wrong description in the database.
CODING TECHNIQUES TO GET
AROUND UCSD LIMITATIONS
There are two places in the implementation of
Adventure 3 where the programming make look
arbitrary at first glance. The first is the main pro-
gram block:
IF ord (location) < ord (steep1)
THEN
t1 (location)
ELSE
t2 (location)
{ END IF ord... }
The second occurs in the initialization part of the
advdata unit:
BEGIN
init1;
init2;
END;
In both of these places there are two proce-
dures invoked. It might seem that either a single
procedure or direct inline code might have been
adequate in both cases. The explanation of the two
instances of strange coding is that UCSD Pascal
places a limit on the size of a procedure. If the
amount of code generated by the procedure exceeds
this limit, the compiler issues the error message:
Error 253: Procedure too long
and refuses to create a.CODE file for the program
or unit being compiled.
When a procedure becomes too long, the solu-
tion is to break it apart. The result may be confusing
when the program listing is read by someone later
on. When you get Error 253 from the compiler,
however, you are much more likely to be frustrated
than confused.
TRICKERY IN showgoodies
The showgoodies procedure, which lists the
objects present in a given location, has been im-
proved as compared to the similar procedure in
Adventure 2. The guide uses good English gram-
mar in the descriptions. This is accomplished by the
use of a few extra set variables declared and ini-
tialized in the cmds3 unit. The variables are called
plurals, somes, and useans and are all of type
collection, that is, SET OF objects. The objects in
Adventure 3 are divided into these three sets to
indicate which pronoun to use when mentioning the
corresponding object. For example, the object keys
belongs to the set plurals. When keys are present in
a given location the guide will say
There are some keys here.
The object leather is in the set somes. When de-
scribing it, the guide will say
There is some leather here.
The object axe is in the set useans— in fact, it is
currently the only object in useans. The guide will
say
There is an axe here.
when describing it. Finally, the object scroll be-
longs to none of the sets. The guide will describe it
with
There is a scroll here.
This is a little tough, admittedly. However, the
existence of Pascal set variables makes it so pain-
less to implement that I couldn’t resist.
253
Writing Your Own Adventures
If you have read this book carefully and studied the
examples presented, you should now be ready to
write your own adventure games. You have all the
tools necessary, and the only limit now is your own
imagination and perseverance. The purpose of this
final chapter is to present several final suggestions.
I shall discuss possible extensions to the adventure
game programs in this book, especially Adventure
3. Finally, before saying farewell, I present a possi-
ble step-by-step approach to the game writing pro-
cess from start to finish.
EXTENDING ADVENTURE 3
There are several directions you might take if
you were interested in extending Adventure 3. You
may think of ideas beyond those presented here.
@ Adding Locations
Adventure 3 has no maze. You might add
one, starting somewhere in one of the two caves.
You might add more mountainpeaks—currently
all the peaks contain important objects of inter-
254
est to the wise man of the mountain. This is
somewhat of a giveaway to one of the central
problems of the adventure. Adding more peaks
and possibly relocating some of the fake or trash
objects to those peaks might make the game
more interesting. You might also add more
nooks and crannies in the general locations of the
game. A final possibility would be to add rooms
inside the monastery—either as a diversion, or
possibly containing one of the wmm treasures.
@ Adding Problems
There are only a few problems in Adven-
ture 3. Adding more would definitely make the
game more interesting. It would also be a good
opportunity for you to practice your problem in-
venting and problem implementing skills. There
is more on possibilities in this area later in the
chapter.
Extending the Map
When you add new locations to an adventure
that uses a descriptions database, you have several
steps to perform and several things to worry about.
1. The new locations must be given names and
added to the descs enumerated types definition.
They should be placed in the section of the
declaration corresponding to actual locations,
that is, before location nowhere.
2. For each new location, a description must be
added to the database. To accomplish this, the
MAKEDESC source file must be updated to
include a placename instruction line for each
new location, followed by the text of the de-
scriptions.
3. The added descriptions must be placed in the
MAKEDESC source file in the same position
and order as the corresponding names were
added to the descs enumerated type.
4. The MAKE80 generator must be run on the
updated MAKEDESC source in order to gener-
ate a new database.
5. All the units and main programs of the adventure
must be recompiled. This is because the descs
types is in the interface of the lowest level unit
in the program—everything else uses that unit.
6. The adventure game must be relinked after all
its component parts have been recompiled.
Adding Problems to Adventure 3
There are any number of possibilities for
adding problems to Adventure 3. New problems
should fit in with the spirit of the game. They should
fit with the setting and the feel of the game. Here
are a few possibilities:
1. The shout command, so far, is used for atmos-
phere only. You could extend it in two ways,
both of which could pose problems for the
player.
First, you could cover one of the important
objects with snow. It would only be revealed if
the player asked the guide to shout in its vicin-
ity. Some of the descriptions already used in the
game have hints to the effect that shouting might
cause an avalanche. The second change to the
shout command would, in the wrong places,
cause an avalanche that buries the player and
ends the game. The two new problems are thus
to discover the buried object and to avoid being
buried in the process. You might modify some
more of the descriptions to insert bolder hints
regarding shouting.
2. The only “roadblocks” in the current mountain
adventure are the trolls and the locked door
leading to the cave under the cellar. You could
easily block other important passages. You
might have a pile of boulders blocking the pass to
an important peak and so on. How such problems
would be solved by the player, I leave to your
imagination.
3. You could make the tooth problem more difficult
by making the tooth appear only once. Then
after a certain number of turns, it would go away
forever.
4. You could require some sort of toll in order to
gain access to the wmm. The first time you visit
his location, if you cannot provide the toll, you
will be rudely dismissed and unable to regain
access to the wmm until you do provide it. If you
fail to bring the toll the first time, you will be
unable to score the maximum number of points.
The toll could involve almost anything. For
example, you might have to discover some se-
cret documents inside the monastery and de-
cipher them to find a magic phrase that you must
intone in the wmm’s presence. I again leave to
your imagination other possibilities.
USING A SKELETON ADVENTURE
Using the term skeleton adventure might bring
to mind another game based on Halloween themes.
However, in this case I have a different interpreta-
tion in mind. A skeleton program is a program out-
line, or partial program, that may serve as the
starting point for writing many different incarna-
tions of the same kind of program. By paring down
the code for Adventure 3 to just those parts that you
are likely to use in every adventure game, you will
arrive at a skeleton adventure. When you begin
writing a new adventure game, you will copy the
skeleton adventure and use it as a starting point. In
each new game, the code you add will differ as will
the game you create.
255
Adventure 3 consists of a main program and six
units. Each of these components can form the basis
of part of a typical skeleton adventure. The main
program retains the idea of a repeat statement with
a CASE nested inside it. The case statement is
based on the rooms enumerated type and invokes
either the travel procedure, to cause a change of
location, or a location procedure, to handle special
action at the location, or a location procedure, to
handle special action at the location in question.
The form of the code is always the same; only the
specifics vary from game to game.
Likewise, many of the procedures and func-
tions of the units cmds1, cmds2, and cmds3 will be
used by every adventure game in the process of
handling commands. The specific commands im-
plemented will vary, but the general outline of the
code that handles them will remain the same. The
same sort of reasoning applies to the units locs,
probs, and advdata.
A Sample Skeleton Adventure
In Appendix A I present a listing of a skeleton
adventure based on Adventure 3 and its division
into units. You may use this skeleton as a basis for
your own games or you may decide to create your
own skeleton based on the techniques presented in
this book and others you invent for yourself.
A SYSTEMATIC APPROACH
TO WRITING ADVENTURES
Above all else, writing adventure games
should be fun. The whole idea is to enjoy yourself.
Create interesting games and let your friends and
family try to solve the problems they pose. You can
add to your enjoyment by having a “system.” Ap-
proaching the software development process in an
organized fashion can help you produce more games
ina shorter period of time, with fewer bugs in them.
The following checklist is only a suggestion.
You will probably deviate from it when you develop
your own approaches to adventure game writing.
But when you do, develop your own checklist. Then
use it when you write your next game. See if you
don’t finish sooner and enjoy yourself more.
256
The Adventure Game Writer's Checklist
1. Draw the adventure map. Use a big sheet of
paper and leave room for notations and additions
and subtractions. You might create fragments of
the map on smaller pieces of paper and transfer
them to the larger map when you are satisfied
with them. I use artists drawing paper bought in
an office supply store for this purpose. Large
size computer printout paper is also good.
Remember to use the tricks I covered ear-
lier. Note places on the map where problems
need to be solved. Number the problems and
either write brief descriptions of them beside
the numbers on the map or somewhere else.
Write down enough when you think of the prob-
lems so that you won't forget them later on.
2. Write the descriptions. When you have com-
pleted the map or at least derived enough of it to
have a good idea of the overall flavor of the
adventure game, sit down and start writing the
descriptions of the game locations. This can be
one of the most enjoyable parts of the game
writing process. You have a chance here to pre-
tend you are writing an adventure thriller. Exer-
cise your imagination. Pretend you are actually
in the adventure game and try to describe the
places as you imagine they would be.
Remember as you write descriptions to in-
clude hints. Problems that are difficult to solve
may involve the player’s discerning these hints
and using them to deduce solutions. Try to make
the hints substantial without being obvious.
3. Create the problems. I have belabored this as-
pect of adventure game writing enough. You
know what to do and how to do it. Just one last
time, I will say, “Be creative!”
4. Decide on the objects for the game. Many ob-
jects may be associated with problems, so you
have a head start on this step. Decide which
objects are essential to the game play and which
are merely decorative or misleading. Try to
keep a balance between the two categories.
Think about where you want the various
objects to be located at the start of the game.
Make notations on your map indicating this in-
formation. Decide whether you want any trick-
ery like that associated with the troll’s tooth in
Adventure 3. If you do, think about how you will
implement the tricky parts.
. Decide which commands the guide will recog-
nize. I have barely scratched the surface of this
subject. There are endless possibilities for in-
teresting commands. Make sure that your in-
tended commands won't be too hard for the
command handling code to decipher.
. Write the Pascal code for the adventure. Com-
pile and link it and debug the program. When this
step is finished, you will have your completed
adventure. Again, you may start with the
skeleton source files provided in Appendix A, or
you may code “from scratch” if you decide upon a
radically different organization for your pro-
gram. Unless you are a very experienced pro-
grammer, the first approach is probably best for
your first few games.
FAREWELL
I hope you enjoyed reading this book, and I
hope you will enjoy writing your own adventures
even more. I enjoy playing adventure games, but I
think writing them is much more fun. If you have
any comments or further interest in adventure
games, feel free to send them along to me. May all
your adventure games run the first time.
257
Appendices
Appendix A
Skeleton Adventure Program
In this Appendix I provide you with the listings of
the skeleton adventure game as discussed in
Chapter 30. These listings are derived from Ad-
SKELETON ADVENTURE MAIN PROGRAM
PROGRAM adventures
venture 3 and are organized into a main program and
several units. The main program and each unit has a
separate listing. There are a total of 7 listings in all.
Ck S4+K)
USES applestuff, More “uses” instructions.
(X#uskl.codek) advdata,
(X#uprobs.cadex) probs, For these and additional “uses” instructions
(xXtucmds3,.codek) cmdss, which appear on pages 262, 263, 273, 274, 282
(xX$ucmdse2.cadexk) cmds2, and 283, refer to “A note about disk Drive
(kK$ucmdsl.codek) cmdsl, numbers in file names and commands” on
(kK$ulocs.codek) locs; page 3 of this PDF.
BEGIN
REFEAT
visitedClocatioang
261
visitedClocatian] + 13
show (location);
es ee rn ea rn ee em es Sen eR eS RT eS os
the following CASE statement caontroals travel
within the adventure. it contains ane case
label for each place in the adventure game.
the procedure “travel’ is passed the
destinations in each of the six directions:
Ma Se@.Walte,d which are reachable from the
lecation represented by the case label. if
the lacation needs "special handling", then
a location procedure ptxyz? is invoked
instead (xyz is the location identifier).
if there are so many locations that this
CASE causes the procedure to become too
large, then a trick like that in adventure
3S must be used to split the CASE into two
ar more parts.
CASE location OF
starts travel (nowhere, nowhere, nowhere,
nowhere, nowhere, nowhere) s
END { CASE location OF};
UNTIL done:
END.
SKELETON LOCATIONS PROCEDURES UNIT
($543
UNIT locss
INTERFACE
USES applestuff,
{tuski.code} advdata,
{$uproabs.code} probs,
{fucmds3.cade} cmds3,
{Sucmds2.code>} cmds2,
{tucmdsil.cade} cmdsl;
{ Location procedures: ptxxx? are declared in this
262
bt te ke ws es
Re het hb ty BS he
63 43
%
2
interface. A location pracedure only needs to
exist if a given location has special handling
that cannot be implemented in ather ways.
See Adventure 2 and Adventure 3 far examples.
ey ree rs
IMPLEMENTATION
{ Put code for the location procedures: ptxxx> here?
BEGIN
END.
SKELETON COMMANDS UNIT 1
+
emdsl.text
Source Files
Ce ae oe
hes bet Fe,
i
i
:
i
i
i
'
t
i
i
H
i
}
i
:
j
i
i
i
H
i
ies |
i
t
:
i
!
}
i
i
t
i
{
i
i
i
i
'
at bd
This unit contains all command processing
suppart procedures and functions. Each
command in the adventure is carried out by a
procedure called ptcmd-,
where
feomd >
stands
far the name of the command,
ks het he he ee he et
as typed by the
player: @.qg. pcarry
{===> carry command.
Some of the ptemd? procedures are located in
this unit,
the athers are in cmds2 and cmds3.
Frocedures and functions like travel,
NOWAY »
docammand,
listen,
and cmdlooakup which are
invoked in order toa recognize the command are
all located in this unit.
1
RS RS ot et Re Re he ke
i
i
i
i
i
t
:
i
t
i
i
H
{
i
i
i
Fy Fn FN SP eS ON ER RS A Se OS eS
{$o+3
UNIT cmedsis
INTERFACE
USES applestuff,
{Puski.cede? advdata,
{$uprobs.code} probs,
{fucmds2,code} cmds2, {Sucmds3.code} cmds3,
{S$ucmds3.code? cmds3; } {Sucmds2.code} cmds2;
cmds3 must be listed before cmds2:
263
PROCEDURE travel (¢
nloc,
sloc,
eloc,
wloc.
WULOC »
dlocs rooms);
PROCEDURE pecarrys
FROCEDURE pdrops:
FROCEDURE pshouts;
PROCEDURE popens
FROCEDURE pwadda;
IMPLEMENTATION
VAR
dchars: SET OF chars
Cc kk p r Q b 1 e nm S
See if the player has solved any problems
because of the command just executed.
eae en ces es
FROCEDURE ckproblemss;
BREGIN
{ This procedure may be used ta check
{ indirectly for the solution of same
{ preblems. Look at Adventure #3 far
{ some good examples.
END ¢ PROCEDURE ckproblems 33
Determine which command the player has typed
and “tail’ for command verbs with objects.
This is the generic command lookup function.
m
5
¢
if
c
.
{ and dissect the command string into *head’
¢
cr
.
£
It assumes the existence of an enumerated
264
et ee het be
hat foat
fod tact
het het
H
BS ee et he
ws he Ls he
type called cmds and an array of strings
called cmdname, indexed by cmds. The string
cmdnamelxyz] contains the string which the
player must type in order to execute
command xyz.
Rt ke eet be ee
Ce a ae he SO oie Sh
at
FUNCTION cmdlookup +: cmds;
VAR
p: INTEGER;
lemd: cmdsy;
BEGIN
writeln; | Capitalize Your’
write (your wish is my command*> *)s;
readin (command) 5
p r= pos (* *, cammand)s:
+.
{ check for verb-object }
IF p = 0
THEN
BEGIN
head == command;
tail s= ""3
END
ELSE
REGIN
head := capy (cammand, 1, pri):
tail := copy (command, pti, length(command)-p);
END { IF p = © 33
cmdnamelnocmd)] s= heads;
lemd :
WHILE head <> cmdnamellemd] Da
lemd s= suce (lemd)
{ END DO 335
emdloakup := lLemds
265
END ¢ FUNCTION cmdlookup 3;
{- cress ogee sens sane seste teave seco svete sete oneee asso aovee cones snese sater onene steuh waver teatd we0ee saves anes ¢uren anene conse soars ennee anene Senne crwss sevee ontse cuptD Ss0e0 Oemme Oxmmb eosse seven scene cecee sauhd Setar crane cosee ~"
{ p $s c Q r e 3
if 3
{ Calculate the number of points scored by the 3}
{ player up to this point in the game. 3
{ aoa see ‘ees’ Sees Sns6h essen Souan Gens @nten oparo'saeke steve esos sane ‘sess cote atmme seese eonse seuse seen sess neon chats eases epube sesse sserd sees asaee ewes iQnimr ets sesee cones S0be0 seete aneee seve areas trees sntes eben sebes Sanne ese }
FUNCTION pscore s INTEGERS;
VAR
re rooms;
keepscores INTEGERS:
BEGIN
keepscore := Of;
FOR r i= start TO wom leftover from Adventure 3;
DO ; change to first and last room names
IF visited{fr] > 9
THEN
keepscore := keepscore + S;
IF saidwadda
THEN
keepscore := keepscore +
(350 ~ ord (fountain) ~- 24);
IF location = fountain
THEN
keepscore := keepscore + 253
pscore := keepscores
END { FUNCTION score 3};
1 i 5 t e n
Dispatch calls to the command execution
procedures ( ptcmd?> as described in the
header comment far this unit.)
ere ere et et
kt et Os ew Ls es
PROCEDURE listens
266
‘saidwadda’
and
‘location = fountain’
are leftover
achievements
from
Adventure 3
VAR
lemd: cmds;
PROCEDURE lscores
BEGIN
writeln (If you should quit now, your score would be
writeln (pscore, * points of a possible 350.*)+3;
END ¢ FROCEDURE Ilscore 3}; —~—
Leftover from Adventure 3
FROCEDURE lquits
VAR
ches CHAR s
fhold response for quit confirmation}
BEGIN
writeln
readin (ch);
IF (ch = *“y"*) OR
THEN
BEGIN
writeln (you would have scored *,
exit (FROGRAM) ;
END {IF (ch = *y’) .
END < PROCEDURE lquit
Care you sure you want to quit?")s
(ch = "y") Capitalize ‘Are’, ‘You’, and the second ‘Y’
pscore,
;
33
ae
7§
BEGIN
REPEAT
turns
lemd
s= turns + 1s
r= cmdlookups;
CASE lcemd OF
a
take,
carry: pearrys
drop: pdrops
help: phelps;
invent: pinventorys Expand list or eliminate commands as needed
Score,
tally: lscores;s
looks plook;
quits Lquits
nocmd: 3
END ¢< CASE lemd OF 33
267
s
4
points.” )y;
ckproblemss
{ See if any problems were solved by the }
{ command issued by the player. 3
UNTIL lemd = nocmeds
END {¢ PROCEDURE listen 33
i
H
;
Call listen in a loop. When the length of
head is greater than zero, the user has given
a travel command so return the first letter
of “head* to the caller.
he Rt eS he het he
es er es ee ee en es
: :
tet hee
FUNCTION docommand :s CHAR;
BEGIN
head r F]
tail a
chgloc : 1
REPEAT
listen;
UNTIL length (head) = O¢
docommand := headCid;
END € FUNCTION docommand 33
me me me te ete et te test eee te ai ee et tte ese nt tt ear nse nts ns tm ese et tne tn tn en ne as se
if w h i c h w a y
£
{ Determine which way the player wishes to qo.
{mm nim mee met mete ne te te ere mee tte ee et ne se Set te tines Sst ae tn se ht te ne ts sf ne
wt wd Re het ke
FUNCTION whichway : directions;
VAR
ch: CHAR;
ww: directions;
BEGIN
REFEAT
268
ch := docommands
whichway #= x4
CASE ch OF
oa ae IF (head = *n’*) OR (head = “north’*)
THEN
whichway i= ms
7s" 4 IF (head = “s*) OR (head = *smuth’*)
THEN
whichway 2 S%
"ey IF (head = *@e*) OR (head = “*east")
THEN
whichway := @5
Pw: IF (head = *w’) OR (head = “*west*)
THEN
whichway #= ws
"urs IF (head = *u’) OR (head = “*up")
THEN
whichway 2= us
"d's IF (head = *d*) OR (head = "down" )
THEN
whichway := ds
"q's { empty for now 35
END ¢<¢ CASE ch OF 33
UNTIL. ch IN dchars;
writelns
END ¢€ FUNCTION whichway 33
[rm mec ee te ee ee tee oe ae te ese ea te te ae aah Sn ne et mt ste tnt snes senses ne ese ttn tn eee tne te sens teat ene
3
¢ n Q w a y 3
{ }
u
PROCEDURE noways
REGIN
269
writeln: J Capitalize ‘It’
writeln ("it is impossible to go in that direction.’);
chgloc := false;
END { PROCEDURE noway 733
A cco naa ibe mabey Seas arenas aera ass esc 608 apenas nasa ceana ous geee sons nen as So tess eae ean foun eee nas Ne
oo 2
£ t r a v e 1 }
f 9,
. P
{ Handle travel to the next location. The }
{ possible destinations fram the current room 3}
{ or adventure location are passed to travel 3
{ parameters. The value “nowhere” means that 3}
{ there is no way to go in the corresponding }
cr %
.o ae
direction.
et
i
rs
4,
FROCEDURE travels
PROCEDURE newloc (loc:descs);
BEGIN
IF loc = nowhere
THEN
noway
ELSE
REGIN
location := 1
ch@lac = trues
END <{ IF lac = nowhere }3
END {¢ FROCEDURE newloc 33
PROCEDURE wingame;
BEGIN
show (fountain) 3 — leftover from Adventure 3, as well as “350” below
writeln (* You scored a total of *, pscore)s
writeln ¢(* points out of a possible 350.")s;
exit (PROGRAM) :
END ¢ PROCEDURE wingame }3
BEGIN CKKKKK to roa vee | kKKK}
270
CASE whichway OF
ns newlec (nloc)s
ss newloc (sloc)s
es newloc (eloc);
we newloc (wloc):
us newloc (uloc)s
ds newloc (dloc) +
x: BEGIN
writeln (*°I do not understand that.’)s:
writeln (’FPlease try another command’) :
END;
END {£ CASE whichway OF 33
END { FROCEDURE travel 13
:
!
i
}
i
i
i
H
i
Lied
p c a r r y
Implement the carry command. Call the
function *objleokup” ta determine which
object (if any) has been requested. Then
call “*ckgoodies* to see if that object is
present in the current location. If so, the
object is added to the set “stash* and also
removed from the set “whatsherellocation]’.
race es rn sy
i
tet Met Bt et bw ht
i
i
f
H
i
aie he ain oe
Bs tt ed Le bet
FROCEDURE pcearrys
VAR
it: goodies;
REGIN
it = objlookups
IF NOT ckgoodies (it)
THEN
BEGIN
write ("I don*"t see any “*)5s
write (tail);
writeln (* here.” )3
END
ELSE
271
BEGIN
writeln ("Ok"):
stash := stash + Citi;
whatsherelLlocation] :=
whatsherelLlocation] - Citds
END ¢€ IF NOT it IN wan 35
END { FROCEDURE pearry 73
{ dt sats seein onhin nei steel otib sb Gants snd: sondn|etosn einen ees sobie to 9s sn mt ne et ut te mt tt tsa ts mt te ns ma ems emt an 4 ae se Ne
{ p d r QO p 3
¢ 3
{ Implement the drop command. similar in 3
{ action to the carry command (q.v.). 3
me oe me ee eet me se te se mie ate re ates ne etm ns si ns te ee mt tn ee ess et ae et te tes me
PROCEDURE pdrops
VAR
its goodies:
BEGIN
it = objlookups
IF NOT (it IN stash)
THEN
BEGIN
write ("You are not carrying any *)¢s
writeln (tail);
END
ELGE
BEGIN
writeln (?Ok*)s;
stash := stash ~- Litids
END: needs an END; statement for IF NOT (it IN stash)
END ¢ FROCEDURE pdroap 33
Additional commands from page 267 would go here.
BEGIN
dchars :=
272
t* a’. *H* 5 a6” e’, 7 yw* a’ ae a: ae eet
‘q’ is unimplemented, see page 269. ‘x’ is a delimiter, or “sentinel”
END.
SKELETON COMMANDS UNIT 2
H
i
|
|
i
i
i
A
!
i
i
i
H
i
3
i
i
i
i
$
tat
t., .
{ }
Lf a
co é
if cm ds 2 uom iat 3
£ 2
Ss -
{ This unit cantains the procedure phelp. The +}
{ help command needs no access to data in unit +}
{ "*advdata’. It dees use procedure *show’, 3
{ however, and hence USES unit cmds3. >
¢ seme sesso seves seute seen nue sess costs svete some sense sense seete sees secon nonue cesen cones tones eevee wenen ettee anees enute pés00 sents seuss Sorse sense cesee sees ensst Gnvue eeses sete seeee G068® Sones cette sees année S1688 seuss ettte sense seems cutee +
C$a+3
UNIT cmds2s
INTERFACE
USES applestuff,
{$usk1l.cede? advdata,
{ucmds3.code} cmdsi;
PROCEDURE phelp;
IMPLEMENTATION
VAR
asked: INTEGERS:
PROCEDURE phelps
BEGIN
IF asked = 2
THEN
show (helpspiel)
ELSE
BEGIN
writeln ("Help is on the way’):
asked := asked + 13
273
END;
END { FROCEDURE phelp 33
BEGIN
asked s= OO;
END.
SKELETON COMMANDS UNIT 3
source file: cmds3.text
;
4%
r
.
c
\
{
{ This unit contains procedures implementing
{ commands. The particular commands implemented
{ herein need access to the unit advdata,
if
i
%
C
not to the unit “probs’.
ad
in
+
we
UNIT cmds3s
INTERFACE
USES applestuff,
{#uski.code} advdatas
TYPE
directions = (Ng Sa Ou Walla aX) G§
cmds = (carry, drop,
take, tally,
pname = STRINGL401;
storyline = STRINGCSOI;
byte = O,.2008
274
Rs eet Lo
SCOres
quit,
but
hs he
be tet ht Go
a
invent,
nocmd) s
whichsect
placerec
(indexsection, descsection);:
RECORD
CASE sections whichsection OF
indexsection: ( tableentry: INTEGER) ;
descsection: ( Names pnames
id: INTEGER:
dbegins INTEGER;
dend: INTEGER:
link: byte);
ENDs
VAR
xfiles FILE OF placerecs;
narrates FILE OF storylines
places: ARRAY ECdescs] OF placerecs:
commands: STRING;
head: STRING;
tails: STRING;
cmdnames ARRAY Ccmds] OF STRING:
chaloc: BOOLEAN:
{ has player moved since last cmd’? 3}
FROCEDURE pinventorys
FROCEDURE plooks
FROCEDURE show (where: descs) :
PROCEDURE showgoadiess:
FUNCTION objlookup: goodiesys
FUNCTION ckgoodies (its goodies) : BOOLEAN;
IMPLEMENTATION
VAR
rs descs;
plurals: SET OF goodies;
SOMES: SET OF goodies;
275
useans: SET OF goodies;
{ for benefit of showgoodies 3
rn
i
!
‘
i
i
!
f
1
f
:
{
i
j
i
\
i
|
if
TI
Oo w
Retrieve descriptions from the database
and display them on the player’s console.
The argument to show is af type “descs’
which includes descriptions of situations
and spoken words as well as descriptions
of locations. Show uses the ord function
to detect what kind of description is
invelved. The description of locations
suppressed if the player has not changed
locations or if the location has been
visited recently. If the player has not
changed location, then nothing happens.
If the player has visited the same place
recently, then just the short description
is displayed.
by hed ae ee
AA AO Me PAN ee Ne el cl eR PA el ot
FROCEDURE shows
VAR
is INTEGER:
BEGIN
IF (chgloc) OR
(ard (where) = ard (nowhere) )
THEN
BEGIN
IF (visitedfClocation] = 1) OR
C((visitedClacation] MOD 4) = ©) OR
(ord (where) * ord (nawhere) )
THEN
BEGIN
WITH placest€wherel] DO
REGIN
FOR i := dhegin TQ dend Da
BEGIN
seek (narrate, i)s
276
Rt het bet BS OS RS A LS A te bs Rs ks
get (narrate);
wreite (narrate™)s
END € FOR i 8 wan F3
END { WITH placestwhere] 33
END
ELSE
BEGIN
write ("You are ")¢5
writeln (placesCwherel.name) ¢
END { IF visitedClacation] = 1
PROCEDURE showgoodiess
VAR
labj: gondiess;
BE G3 Ni
FOR lobj := nuggets TO noaobj DO
IF lobj IN whatsherellocationg
THEN
BEGIN
IF lobj IN plurals
THEN
weite ("There are same *)
ELSE
IF lobj IN somes
THEN
in
ma
END { IF chgloc 33
IF ord (where) «< ord (nowhere)
THEN
showgoodiess:
END { FROCEDURE show 33
Lm con eevee veves sesen seven seees eeu seene ssenw sents samen eevee seven anon seeee steed conte stsee conen cunts coven coeun svees stunt C0088 O008e orntt HeOtD evese svete sttt® stess stete Sune ste cites atnin Seuss anaes ceser eutte cutee
{ s h oF w qooadiéeoe
{ Print a list of the objects present at the
{ current adventure game locatian.
[em mee mim me ns mess esteem nes eee tne sm ut ses mn ts me stm out meme sey meee nen neses mee se nome te ete tt mat me tt mim er ees mee
bs he et be
Le tes
277
write ("There is some *)
ELSE
IF lobj IN useans
THEN
write ("There is an *)
ELSE
write ("There is a *)
{ END IF lobj IN useans }
{ END IF lob3j3 IN somes }
{ END IF lobj IN plurals 3};
write (objnamelLlabjd)s:
writeln (* here." )4
END { IF lobj IN whatshere... }
{ END FOR lobj s= wa. 34
END € PROCEDURE shoawgoodies 33
{ sus soak sbd sanen esc nes suse shane oun Set esas samen see anes ates ts dete cuacn Sasa asus eng eke Sounds ea osnan aante pis avees oimna Gases anene ene aesse| Somas ais qusna inan eases gents spe 3}
{ a b j 1 Q a k ul p 3
t +
{ Determine if the name typed by the player }
{ in a carry ar drop command is the name of 3
{ any object actually part of the game. If }
{ so, return the internal value of the }
{ abject in question. +
{ es pert ee st ee sn ae ene ene et snes nese ne ee ht te tes tne meses t anesne ee ans te sne pn es sam tens tnt ns mts eae mo te ne Ye
FUNCTION ob jlookups
VAR
lobj: goodies;
BEGIN
ob jnamelCnoobj] := tails
lobj := nuggets; €—— ‘nuggets’ is leftover from Adventure 3,
it should be replaced with first object name.
WHILE tail <> objnamellobjJ DQ
lobj s= succe (lobj)s
ebjlookup := labijs
END ¢ FUNCTION ob jlookup 335
Ch ae Sh
Mm
—
0
S
a
mt
By
HH
het es
{ See if an object is present.
P
o eae , Seow S Reiner!
he ke
FUNCTION ckqoodiess
BEGIN
IF it IN whatsherelLlacationd
THEN
ckgoadies := true
ELSE
ckqgoodies := false
{ END IF it IN wan 33
END € FUNCTION ckobject 33
{ om en ry ceoee cates ont Reng aia i ai js ose: basen wend i pom sos ages ston 09 }
if p 1 c Qa I +
{ Implement the look cammand. -This forces 3}
{ out the full description of the current 3
{ lacation. The variables chglac and 3
{ visitedCLlocatiaon] must be temporarily +
{ reset in order to accomplish this goal. 3
{ oosen euite aston cased sabe eaten thse seen Skee otibo eabgn des ionsen shane] tieoe' amity) stucs sawn, diabé bas lea /eaais Sects sew cess iceman decal cue Sends srw baw vee Sewh sbteo’ Sages exten onsee'esuem Sinbaraeb@ sobes coins ~
PROCEDURE plooks
VAR
savchqs BOOLEAN:
Savisits INTEGER:
BEGIN
savchg := chgloc;
chgloc = true;
Savisit := visitedClocationg;
visitedClocation] := 1;
show (lacation) ;
VigsitedClocationd :
chgloc t= savchgs
END ¢ PROCEDURE plook 3;
- ‘ = yen oe a scat ede asia gan. Saban ca ay
for rae triennial SU nl aie od nae a aa cin bee int enian ses}
{ p i n Vv e n t ta) r y }
{ 4,
a
279
{ Implement the inventory command. Frint 2
{ the names of all objects carried by the }
{
player.
en
FROCEDURE pinventorys
VAR
lobj: goodies:
REGIN
IF stash «3 £9
THEN
BEGIN
writelnm ("You currently holds *)5;
FOR lobj := nuggets TO noobj DQ €—— ‘Nuggets’is leftover from Adventure 3,
BEGIN _ replace with first object name.
IF lobj IN stash
THEN
writeln (objnameLlahj I)
{ END IF 33
END ¢ FOR lobj :=
END ¢ IF stash ¢ > CJ 33
END ¢ FROCEDURE pinventoary 3
BEGIN
Plurals s= € { Put in abjects whose names
are plurals. 34;
Fut in objects whose
descriptions should begin
with "some" : there is some
Silver here. } J;
Fut in objects whose names
begin with a vowel. 3} Js;
ey
Ss0mMes := OC
Cae)
useans s:= 0
comdnamelCcarry] 2= “carry"s
cmdnameCdropd = “drop’s
cmdnamelChelpd 2= "help",
cmdnamelinvent] = "inventory’ ;
cmdnameCtallyd r= “tally’;
cmdnameltaked = “take’s;
cmdnamelscore = "score;
cmdnameClookd = Look’;
Add or eliminate commands as needed
280
emdnamelCquitd
cmdnameCnocmd i
reset
reset (narrate, "skel’)s;
aa
r= "quit? s
:= “sentinel”;
‘skel.x’ and ‘skel’ are placeholder names for the
= start:
seek (xfile, 31)4
get (nfile)s
pl
acesCrJ] s= «file;
REFEAT
UNTIL r = lastdesc;
{
‘
ros = succe (rds
get (xfile);
Places(fr] xfilevs
lastdesc represents the last identifier in
the descs enumerated type.
close (xfile);
chqloc #= trues;
£
(s
END,
Force aut descriptian of start 3
SKELETON PROBLEMS UNIT
af
ea oes eA es me rn ee en eR en eS et es en ts es
:
this unit contains the single functians
solved. solved is passed an argument of type
*"problems’*. the arqument corresponds ta ane
the boolean expressions used to detect the
saqlutian @f problems and camponents of
problems. the rest of the adventure code
uses cade likes
IF NOT solved (luretrolls)
THEN
instead af using the complex expressians.
the details of the problem expressions are
(xfile, ‘skel.x’)3 } database files needed by your adventure.
See “A note about disk Drive numbers in file
names and commands’”on page 3 of this PDF.
Replace lastdesc with the last description database entry
as described below. (in Adventure 3 it was ‘helpspiel’)
ws ke
MA bs Be Re RS bs RS RS Ss ts hs ke ee ks ke he ke
281
{ contained here and may be changed without }
{ forcing the rest of the adventure game code 3
{ change as well. 3
{ }
econ noes Ste Sates ae alone cree ees stene mane one wanes on spt Saisie sow ans nuovo tebe esas ee es Gs cme essen ae ae nee),
{$s+}
UNIT probs;
INTERFACE
USES applestuff,
{$uski.code} advdata;
TYPE
seus nese conse cours come scan weuee comes oveve eeene seste comte estes cope canes conse seecs gruee sume cece fests s0eee ouctn 060s arom= seems Gftcs cone costs camen aovae sects
en
i
H
}
i
i
|
t
i
i
i
The problems enumerated type declares
identifiers that are used in representing
components of problems. Each identifier
has a Boolean expression associated with it.
When that expression needs to be evaluated
anywhere in the Fascal code of the game,
the function *solved*® in the INTERFACE of
this unit is called with the identifier of
that expression as its argument:
solved (probx)
RS tet bt RS A Bw ee Re ew et et et be
ten smete save arses sete anees sone neces samme 14406 coume seats meses onves seast seems neuen memes ovsse segue sven sees ¢tete appt nome sunte tenes regne seta tteun nent atues W190 sumed feet suse coeat seen teen sents Jenee cumne mney sense omnes
Arm ee ee es el ra et fk
problems = (probi, prob2, probS, prob4)s: placeholder names for problem names
FUNCTION solved (which: problems) : BOOLEAN;
IMPLEMENTATION
FUNCTION sol veds
BEGIN
solved := FALSE;
CASE which OF
282
{ Each problem identifier has its boolean 3
{ expression inserted in the corresponding }
{ CASE in this statement. +
probl,
prob2, problem names and
prab3, boolean expression
prob4;: solved := TRUE; are placeholders
END {¢€ CASE which OF 3;
END { FUNCTION solved 3;
BEGIN
END.
SKELETON ADVENTURE 3 DATA
sce: wanen goons saben Sey ones tau 0b opeee Seine, cee shen tanib, Sores name ese rden Soles \Senew susie sbopesovob shean seue’ seen soe
\ 2
{ source file: ski.text }
£ $i Saas sai iba ees us as: Sones wana dann Slit nan ts 6 Gb cs ee Goes vest rapes 3
{- sata ints nnn at iene nse yin inaie sents swose sonwe emome aiie woven Séuho Seven seert. satnk Sesto sures ot ys cd nbn cb: ibn abe nie svchg uum Ss coum Sb sinc Speeds enna eo tahoe nee
£ 3
os Z
{ roo m 5s uom it 3
£ *
a Z
{ Types and variables used by all other units }
{ ain skeleton adventure. This includes the ?
{ descs enumerated type, extended toa account 3}
{ for some descriptions that are nat actual 3
{ qame locations. The goodies type accounts }
{ for all objects in the adventure and the >
{ objname array contains names for all of }
{ them (for use in descriptions and in 3
t commands). -
f *.
. 2
AO each caw ace lattes ta nies ee aan. me aes a ois cee Sanaa bas Ss hrs sunt ames wane aw ves ad vs ne Swe Swov ea whan Invi vedas a os abies BS
. J
UNIT advdatas
INTERFACE
USES applestuffs
283
TYPE
descs = (start, nowhere, special, helpspiel):;
rooms = start..nowheres
goodies = (lamp, noabj);
collection
VAR
lacation:
= SET OF goodies;
roams:
stash: collections;
whatshere: ARRAY Erooms] OF collectians
visited: ARRAYEC rooms] OF INTEGER;
ob jname: ARRAY EC goodies] OF STRINGLISI;
turnss INTEGERS
done: BOOLEANS
Add more VARiables as needed
FUNCTION rand (low, high: INTEGER) : INTEGER;
IMPLEMENTATION
VAR
rs roams:
{ loop control for initialization }
FUNCTION rands
VAR
mi. Ca Ge INTEGER:
BEGIN
rand s= Of;
IF low = high
THEN
rand := law
ELSE
BEGIN
c r= high ~ law + 1;
mx os= (maxint ~ high + low) DIV c + Ls
284
These three lines contain
placeholders to be
expanded, replaced, or
removed as needed.
(‘nowhere’ and ‘noobj’
are delimiters,
aka sentinels.)
myx s= mx xX
REPEAT
do: = randam
UNTIL do <= mxs
rand := low + d MOD cs
END ¢ IF
low = high 33
END ¢ FUNCTION rand 3};
BEGIN
locatian := starts
ob jnameClamp] := “lamp’s
FOR ro := start TO chasm DO
BEGIN
whatsherelr] :s= C4,
visited(rd r= Og
END ¢ FOR ro s= start one 33
whatsherelstart] := Clampds
stash r= Cs
turns r= O8
done = false;
Expand list as needed
END.
low) +
tmx oo 1)s
Adventure 3 splits this into two procedures because it was too long for a single
procedure in Apple Pascal. See page 253 and 236-238 for more information
When game begins, sets current location to room named ‘start’
Defines full name of ‘lamp;’ as ‘lamp’, see pages 236-237
Clears rooms of objects and clears “visited” table
(first room to last room, start to chasm,
which are leftover names from Adventure 3)
Puts lamp in “start” location, see pages 237-238
285
Save as “A2.DB80.TEXT” (per page 83)
See Chapters 18-23, pages 145-189,
for instructions on making the database files.
See page 170, “CONTINUATION FILE LINES”
if you cannot fit the entire database into a
single text document.
Appendix B
The Adventure 2 Database
In these final appendices I provide you with the
source for the descriptions databases used in Ad-
ventures 2 and 3. This source is for use with the
MAKEDESC program described in the text.
In order to make the text fit on the printed
page, the long lines of description used in the
MAKEDESC source have often been split into two
SSTART
parts. Every place this has been done, the end of the
first line of the resulting two lines has been marked
with a plus sign, +. Should you type this into your
computer system, remove the + sign from the ends
of such lines and “tack on” the following line to form
a single line of close to 80 characters.
You are standing by a hole in the ground.+
It looks big enough to climb
down.
SGRANDROOM
You are in a huge apen roam, with an immenset
xpanse of ceiling. A dark
passage leads west and a narrow crawl+
leads downward.
$VESTIBULE
You are in the entrance to a cave of passageways. +
There are halls leading
off to the nerth, south, and east. Above yout
is a tunnel leading to the
surface.
286
SNARROW 1
You are in a marrow passage that cantinues tot
the north. It is extremely
narrow ta the south. A very tight crawl alsat
leads east. A curious odor
seeps through it. I would think twice beforet
trying ta go that way.
SLAKESHORE
You are an the shore of a vast underground lake.+
Narrow passages wind away to
the east and south. A small island is visiblet+
in the center of the lake to
the north.
$ISLAND
You are on a small island in the center of at
huge underground lake. Dark,
frigid waters surround you on all sides.+
You can barely make out the
shoreline to the south. A message is scratched+
in the dirt here. It says:
"The treasure may be found in the maze.’
SBR INE
You are an the brink of a steep incline.+
The bottom of a pit is over fifty
feet below you. You could probably slide+
down safely, but I woan’t promise you
that you could climb back up. To the west is at
rubble-filled tunnel. A
vampire bat just flew out of it shrieking.
$I CEROOM
You are in a room whose walls are formed fram+
a deep blue crystalline ice. To
the north a narrow tunnel opens. From the oather+
end of this tunnel an ominous
growling sound may be heard. To the east at
sparkling luminesence emanates
from a braad opening. To the west a passaqget
leads back to the vestibule.
SOGREROOM
You are in a low roam whose walls are covered+
with grisly dark gouts of dried
blood. The center af the room is dominated byt
a firepit, which contains
burned out coals and a long spit suspended over+
its center. From one corner
emanates a horrible gqrawling noise like that of+
287
some unspeakable monster
dreaming af rending you limb fram limbt
and making you its dinner.
$NARROW?
You are in a very narrow east/west passage.+
To the west the passage opens
out by a lake shore. To the east it becomest
even tighter than it is here.
You might be able to squeeze through if you try+
real hard. There is also a
strange looking alcave in the south wall that+
seems to open into a very dark
tunnel.
SPIT
You are at the bottom of a fifty foot pit.+
The walls are just a hair too
steep to climb. The pit is empty except far at
few old dried bones. I can*t
tell whether they are human or not!! In thet
center of the pit is a narrow
shinny leading further downward.
SCRYSTAL
You are in a dazzling hall of crystal glass.+
It is intensely cold here, but
also chillingly beautiful. There are glasst+
formations rising from the floor
as if they had grown there, yet delicatelyt+
sculptured with multi-faceted
sides. A searing white light shines+
blindingly through the floor, itself
formed from fine mirror smooth lead crystal.+
The light sets off reflections
that corruscate through the room and maket
it virtually impossible to tell
where the room begins and where it ends.+
There might be an exit to the east,
although my eyes could be betraying me.
SRATSCAVE
You are in a steep cavern filled witht
shrieking vampire bats. They swoop and
dive at you by the thousands. I would+
suggest a hurried departure. There are
openings ta the west and nerth.
$STEAM
You have entered a roam filled with at
stifling steamy vapor. There are
288
innumerable small qeysers scattered about+
the floor of the room, each
cantributing its own steam ta the general mist.+
To the west is a dark opening
and aneather ta the narth. Further out in thet
middle of the roam an opening in
the floor is barely visible through the mist.+
Some of the vapor seems to ooze
through the apening as if it continued downward.
$DEADEND
This is a dead end. There is a cryptic messaget
etched in the stone of the
walls here. It reads: +
7"GOOD THINGS GO THROUGH SMALL FASSAGES. °
$l. ADDER
You are at the base of a huge ladder reaching upt
and out of sight. It must
extend up at least SOO feet. It will take+
someane very brave in heart toa
scale it. There are also passages here whicht
lead to the north and down.
SMAZE
You are in a maze of featureless passages.
$STAIRS
You are on a steep stairway of rough-hewn+
granite blocks. Your lamp only
penetrates the glaom for a few feet in either+
direction. Above you is a
doorway leading to a large room. Helow is at
dark and drafty continuation of
the stairway.
$ECHOES
You are in a vast underground cavern. Thet
slightest sound seems to create a
resonating wave of echoes. It is difficult tot
even hear yourself think. To
the north an opening leads to a downward+
slanting corridor which quickly bends
out of sight. In the center of the cavern,+
there is a set of rough stairs
leading downward. Behind you a similar sett
of stairs leads upward.
SWARMROOM
You are in a confined space which is more liket
a widening of the corrider than
a room. The corridor itself ends abruptly here,+
289
but a narrow shaft opens
downward. There is a faint red glow to be seent+
if one peers into the shaft
and a warm draft of slightly sulfuroust+
smelling air emanates from that open-
ing. The corridor slants downward to the south.
$ INCLINE
You are in a twisting, downward slanting corridor.+
The corridor forms a
Y-shaped intersection here with passages leadingt
up, east, and west.
SHONE YCOMB
You are in a room with numerous honeycomb-Llike+
openings leading out. The
largest of these appears to lead south.
SROUNDROOM
You are in an almost spherically shaped roamt
with exits to the west, north,
and down.
SMUDROOM
You are in a room filled with thick, knee-deep mud.+
Trickles of water flow in
from a narrow opening high above you on thet
west wall. A passage leads up
from here and another down.
$DEEFFOOL
You are standing by a deep underground pool.+
The water is ice cold, but clear
as crystal. There is nary a ripple an the surface.
$COLDROOM
You are at the bottom of an icy cold underground+
pool. If you don’t get out
quickly, you’ll either drown or freeze to deatht
or both.
SNARROWS
You are in a slanting N-S passage.
SNARROW4
You are in a sloping N-S passage. A narrow slitt
opens downward, from which
emanates a warm, sulfurous draft. A very faint+
red glow may also be seen
through the slit.
SRIVER
You are standing on the eastern shore of a swiftlyt+
running N-S underground
river.
290
$ROCE YROOM
You are in a room filled with irregularly shaped+
boulders. To the east a low
opening below a huge boulder leads to ant
alcove of some sort, and to the south
the passage continues along the river bank.
$SILTROOM
You are near a large underground deposit of silt.+
A passage leads upward.
From it steadily oozes a flow of thick mud which+t
must contribute to the
deposit. Passages also lead north and west.
SAL COVE
You are in a small nook off a large room+
filled with boulders. The only exit
is to the east. Toa the west you can see intot
the boulder room, but the exit
is blocked by a large, slippery boulder.+
A note here says: KEEF ON DIGGING!
$FLAMES
Unfortunately, you have fallen into ant
underground lava pocket. It is the
source of the heat that produces the geysers+t
in the steam room. You have
been, to put it politely, “toasted to a crisp!’
291
Save as “MTADV.TEXT” (per page 232)
See Chapters 18-23, pages 145-189,
for instructions on making the database files.
See page 170, “CONTINUATION FILE LINES”
if you cannot fit the entire database into a
single text document.
Appendix C
The Adventure 3 Database
fat the spectacular mountain view
You are in mountains. To see all the snow-capped+
monsters hereabouts,
you would need to look up and turn in a full+
circle. Near here is a
deserted monastery. It is said that a sect of+
mountain worshippers once
lived there. It is reputed that they have hiddent
various treasures in
these parts. It is also said that somewhere int
these mountains there
lives a wise seer who knows the secrets of life.+
Ferhaps you will be
able to locate him. Should you succeed int
finding him and learning his
ways, you will receive many rewards. Watch out+
for treacherous passages.
Do not necessarily believe all you see and hear.
fwalking along a steep mountain trail
You are walking along a steep mountain trail,.+
The footing is tricky
here. Several snow-capped peaks are now visiblet+
292
in front of you.
f$walking along a narrow mountain trail
You are walking along a narrow mountain trail.+
The footing is tricky
here. Several snow-capped peaks are now visiblet
in front of you.
fon a steep mountain trail
You are walking along a steep mountain trail.+
The footing is tricky
here. Many snow-capped peaks are now visiblet+
in front of you.
fon a narrow mountain trail
You are walking along a narrow mountain trail.t+
The footing is tricky
here. Many snow-capped peaks are now visiblet
in front of you.
$walking along a steep trail
You are walking along a steep mountain trail.+
The footing is very
tricky here. You see snow-capped peaks int
front of you.
walking along a narrow trail
You are walking along a narrow mountain trail.+
The footing is very
tricky here. You see snow-capped peaks int
front of you. I seem to
remember having been here before.
scrambling through a rock-strewn col
You are scrambling through a rock-strewn col.+
Above and below you are
ledges that look safer than where you are now.+
Every now and then I
hear faint echoes of churlish voices. It ist
difficult to tell fram
which direction they come.
$in a tricky, rock~strewn col
You are trying very hard not to lose yourt+
footing. This is a very
tricky, rock~strewn col. Above and below yout :
are secure looking “fairly”
ledges. They are quite a distance in either+
direction, but I think
you could make it to either one safely if yout
are careful.
in a col with ledges above and below
You are scrambling through a rock-strewn col.+
293
Abave and below you are
ledges that look safer than where you are now.+
Every now and then I
hear faint echoes of churlish voices. It ist
difficult to tell which
direction they come from.
$in a rocky col
You are in a rocky col. It is difficult tot
see anything from here as
yau cling to the mountain. The air is cold+
and the wind whistles as
it blows across the bare rock~scape.
$in a rubble filled hollow
This is a rubble filled hollow. Talus from thet
higher points in the
surrounding terrain has accumulated here over+
the eons. It is quite
difficult to maintain your footing in places.
$in a hollow filled with rubble
fat a grassy mound
Yau have surmounted a grassy maund. There ist
a pleasant meadow here
filled with colorful wildflowers. Thet+
fragrance is stimulating. I
think I see an ancient looking building in thet
distance. It is
partially hidden from view.
fat an ancient monastery
You have found the ancient monastery. Thet+
building is partially
collapsed and all the entrances have beent
blocked with undergrowth or
boarded up with great hand~-hewn planks of what+
looks like very hard
wood. The tatters of what must once Waa been+
a flag flutter in the
light breeze from a high and pointed parapet.+
There is almost total
Silence here apart from the sighing of the wind+
and the noise of birds
and small animals. Yet there seems to be at
ghostly echo of gongs,
cymbals, plaintive and monotonous chanting, and+
a rushing noise like
fire consuming an enormous pile of dead branches+
294
and leaves. It leaves
an impressian in the mind alone ~- the ears heart
nane of it.
The building leans into the sides of the steept
mountain path. Leading
to the now blocked front entrance is a set of+
broad wooden stairs. Set
inte the bottom of this staircase is at
windowless wooden gate. Tt is
hanging askew from its rotten hinges ~ I think+
you could squeeze by it
if you wished ta.
$in a dank and musty cellar
You are in a dank and musty cellar. It looks+
like it might have once
been a storeroom for the monastery. There aret
empty barrels standing alang
the back wall. There are rows of dusty shelves, +
mostly empty save for an
accumulation af cobwebs. In the floor are twot
trapdoors or hatches made
of the same rough-hewn planks seen outside.+t
They both shut tightly.
Sin an ancient catacomb
This 15 an ancient catacomb directly under thet
monastery. The light is
extremely dim caming only from the cellar ahovet
down the steep stone
staircase. The floor is littered with bones.+
Bits of what appears to
be dried flesh still cling to them in many cases.
fon a steep mountain slope
You are on a steep mountain slope. There is not
vegetation here, you
have passed beyond the treeline. The baret
granite peeks out here and
there from beneath the fresh dusting of snoaw.+
There are massive drifts
to either side. Tread lightly, avalanches aret
easily caused here by
sudden noises.
fan a slope beyond the treeline
You are on a steep mountain slope. There is not
vegetation here, as you
have passed beyond the treeline. You see bare-+
granite here and
295
there fram beneath the fresh dusting of snow.+
There are massive drifts
on either side. Tread lightly, avalanches aret
easily caused here by
sudden noises.
fon a slope surrounded by drifts
You are on a steep mountain slope. There ist
not any vegetation here.
You are past the treeline. Kare granite peekst+
fram beneath the fresh
SNOW. There are also massive drifts to eithert
side of the marrow trail.
I would be extremely careful about loud+
noises ~ avalanches have been
known to be caused by incautious travelers.
fon a slope bereft of vegetation
You are on a steep mauntain slope. There ist
not any vegetation here.
You are past the treeline. Hare granite peekst+
from beneath the fresh
SNOW. There are also massive drifts to either+
side af the narrow trail.
I would be extremely careful about loud+
noises ~- avalanches have been
known to be caused by incautious travelers.
fon a slope with granite underfoot
You are on a steep mountain slope. There ist
not any vegetation here.
You are past the treeline. Bare granite peekst
from beneath the fresh
SNOW. There are also massive drifts to either+
side of the narrow trail.
I would be extremely careful about loud+
noises - avalanches have been
known to be caused by incautious travelers.
$in a deep ravine
You are in a deep ravine. All you can see ist
the tattered clouds
above. They are racing along beneath the bluet+
sky, carried by the
calamitous mountain gales.
$in a deep crevass
You are in a deep crevass. There is a ridqget
above you ~- I think you
might be able to scramble back up with some effort.
fin a bitterly cold deep ravine
296
You are in a deep ravine. Al1I you can see ist
the tattered clouds
above. They are racing along beneath the bluet
sky, carried by the
calamitous mountain gales. Although the sunt
appears now and then
it is bitterly cold here.
fon a steep pitch
You are on a very steep nitch. The bare rock+
beneath your feet is
treacherous in the extreme. Any false movest
here and you will not live
to tell your grandchildren about them.
fon an extremely steep pitch
fon a very steep pitch
fon a steep pitch with bare rock
$on a pitch with bare rock
it)
fan a mountain peak
You have reached a magnificent mountain peak.+
All around you lies the
grandeur of the mountain range. You can seer
several other peaks. They
look so near you could almost reach out and+
touch them, yet they must
be several miles away, even as the crow flies.+
The wind here howls in
your ears, but does not diminish your pleasuret
in the vista.
fon a splendid peak
fon a magnificent peak
fon a grand mountain peak
fon a peak
atop a saddle like formation
You are atop a saddle like formation.+
In some directions there are
dropoffs and in others the trail continues upward.
fon a saddle
297
fatop a saddle
$in a mountain cave
This is a mountain cave. It continues on intot
the heart of the
mountain. The passageway is very narrow and+
filled with debris.
There are pits in the floor of the cave.+
Some coantain the remnants
of fires: charred wood and ashes. Some of thet
wood is very strange
looking ~ sart of whiteish and quite lang and narrow.
$deep in a cave
You are in a cave in the very roat of thet
mountain. It is pitch
black in here. I wouldn’*t stay long ~ there are+
nameless things which
see in the dark.
Sin a dimly lit mountain cave
This is a mountain cave. The light here is dim,+
but further along
the passage there appears to be an opening from+t
which it issues.
There is a draft coming from the darker side of+
the passageway. It
whistles along and now and then seems to whisper.+
You can almost
make out faint words, but they make you shudder+
and want to turn
away .
$in a cave
This is a mountain cave. The light here is dim,+t
but further along
the passage there appears to be an opening from+
which it issues.
You feel a slight draft on your face.
in a cave in pitch dark
You are in a cave in the very root of a mountain.+
It is intensely
dark here ~- you cannot see a hand held inches fromt
your face. AS you
move, you brush against tendrils of a featheryt
substance. It makes
your flesh crawl.
$in a cave with bones strewn about
This is a mountain cave. There are remnants oft
298
cooking fires here.
There are old bones strewn on the floor.+
There are strange looking
runes or symbols of some sart carved into the walls.
$in a shallow gully
You are in a shallow gqulily.+t+
It seems to deepen further ahead.
Fin a deepening qulily
You are in a deepening gully. In onet+
direction it seems shallower.
I can’t say for sure, but in the other directiont
there may be an
opening into the side of the mountain.
$on a barren hill
You have surmounted a barren hill. There is thet
barest hint of foliage
here, but most has succumbed ta the rigorous+
climate and the altitude.
fat a sheltered place
This is a sheltered place in the path. It is at
very deep cut in the
side of the mountain, not quite a cave, but mucht
mare than a mere
depression.
fon a narrow ridge
This is a narrow ridge. The trail is only'’a foott+
or so wide here and
negotiating it will be quite a challenge.+
There are precipitous
dropoffs in some directions and the chasm yawns+
beneath you.
on a very narrow ridge
This is a narrow ridge. The trail is only a foot+
or so wide here and
negotiating it will be quite a challenge.+
There are precipitous
dropoffs in some directions and the chasm yawns+t
beneath you.
fat the grandest peak
You have reached the grandest peak in the entiret
mountain range. Here
there is almost an entire acre of flat from whicht
to view the
surrounding peaks. All of the other pinnacles+
are spread out beneath
the one you have reached.
299
On the far side of the small flat of the peak ist
a wizened ald man.
He appears to have been here quite same time.+
Perhaps he can tell you
some interesting tales.
$chasm
You have fallen off the mountain side.+
The chasm that yawned beneath
you has claimed you for its own. The drop of+
several thousand feet may
not have been enough ta finish you off, but thet
sudden stop at the
bottom certainly was.
Snowhere
$cellardawn
fat fountain of youth
You have located the fountain of youth.+
Never a wrinkle shall grace your
countenance. Your quest is culminated.+
Cangratulations.
Strtalk
There are several huge, ugly mountain trollst
lolling nearby.
Unfortunately, they have spotted you.+
Quickly and rudely you are
surrounded and bound with rough thangs of+
ragged and grisly leather. No
doubt the remains of the trolls last and+
(for them) meagre meal. Now
they have yau!!
With great gusta they truss you up to at
gnarled and charred hardwood spit.
This, of course, is accompanied by flayingt
you and using the strips of your
flesh to make your bonds even more secure. +
They then slowly roast you to
a turn over their hastily built fire.+
You make a fine meal for these
famished, gluttonous villains.
The last sounds heard by your erstwhile, +
but discretely hidden, guide are
those of vast troll cantentment. Snores,+
great rude belches, sSlavering over
the last remnants of roasted flesh an what+
were your bones, and other more
unspeakable sounds. I beat a hastyt
300
retreat from this gruesome location and
bid you better luck in your next+
re-incarnation.
Swmmb 1 ab
The wise man of the mountain speaks:
*T am exceedingly pleased at your obeisancet+
and your gifts. In return
for your devotion, I offer you a word.+
That word is: wadda. Use it well,
my san.”
Swmmharp
The wise man of the mountain lifts a deept
hood covering his face. He gazes
at the items arrayed before him and speaks:
"You give me great displeasure oh profligatet
servant. Of silver and gold
I will have none. Treasures of the earth+t
are but transitory baubles to be
scorned and discarded. Take them from my+
sight and bring me gifts of worth
to the spirit. I would sooner have one molart
from the jaw of a troll than all
these worldly goods. If you seek my wisdom, +
you will heed this charge.’
S$wmmhella
The garbed figure cames toward you.+
He speaks:
"Welcome, my child. You have achieved much+
simply to reach my mountain peak.
If you bring to me the treasures that I seek,+
then you shall know the secret
of everlasting youth! Ga now and fulfill+
your quest. *
fwmmsp 1
The wise man speaks:
"So you return, my child. I have beent
exceedingly occupied by my efforts at
omphaloskepsis. You will excuse me if I+
do not linger.’
fSwmmsp2
The wise man speaks:
"A seer once told me that only those whot
persist succeed in finding the true
secrets of existence. Do net despair, yourt
quest has just begun. You have
much to learn and much to gain. Io must+
301
7
now feed and tend my yak.
fwmmsp >
The wise man speaks:
"Have you read any goad scrollst
lately my child?’
Swmmsp 4
The wise man speaks:
*Cantemplation is good for the soul, myt
child. But music and prayer also heal
many wounds. *
Swmmsps
The wise man speaks:
"All that is gold does not glitter,+
my child. A little wine for the stomach’s
sake. A stitch in time saves nine.t
Don*t take any wooden nickels. A flush
beats two pair any day, but then I don’*t+
have indoor plumbing up here. Am JI
boring you? Maybe I should go say my prayers.’
$tgablure
The trolls seem very excited. I can*’t+
tell what they want more ~- you or
those bones you are carrying.
$tqabbones
The trolls seem to have lost interest+
in you ~- at least for the moment.
They are gathered around the bones+
you have dropped.
Wait... They are attacking the bones+
with great gusto. There are crunching
noises and sounds of breaking teeth.+
I would take this opportunity to make
tracks.
$trhungry
Several huge, ugly, mountain trolls aret
lolling nearby. Unfortunately,
they have spotted you. They slaver andt
utter gutteral blasphemies. They
gaze longingly at the bones you aret
carrying. They rise. They seem to
want to follow you. I would find a qood,+
out of the way place to ditch
those bones if I were you!
$trfollow
You are being shadowed by several huge,+
hairy, rude, and hungry mountain
302
tralls. They slaver and mumble in their+
gutteral speech. You can barely
understand, but the general idea is that+
they are famished and want bones
and flesh. Perhaps it is yours+
that they will get!
Write your own “helpspiel” and add it to the end of the description database:
Shelpspiel
The Abominable Snowman arrives with a tray of snow cones. He made them
himself! After a delicious treat and a hearty conversation about the many types
of snowflakes, he apologetically explains that he knows only as much as you about
the Wise Man of the Mountains and the Fountain of Youth, and he soon departs.
303
Index
Index
A
Adams, Scott, 1
Adventure, 1
Adventure code, 191
Adventure databases, structure of,
177
Adventure language, 191
Adventure on disk, 191
Adventure 1, 15
Adventure 1, data declarations for, 18
Adventure 1, functions and proce-
dures, 18
Adventure 1, listing for, 23-53
Adventure 1, program code outline for,
17
Adventure 1, structure diagram, 21
Adventure 1, the map of, 55
Adventure 1, the maze map of, 56
Adventure 3, 192
Adventure 3, code outline of, 193-195
Adventure 3, database for, 250, 292
Adventure 3, extending, 254
Adventure 3, listing for, 203
Adventure 3, map of 197-201
Adventure 3, problems in, 245
Adventure 3, problems of, 201
Adventure 3, program outline of, 193
Adventure 3, relation of units for, 196
Adventure 3, summary map of, 196
Adventure 3, units in, 239
Adventure 2, 68
Adventure 2, database for, 286
Adventure 2, data declarations, 70
Adventure 2, listing for 78-114
Adventure 2, main program block, 73
Adventure 2, map for, 75, 76
Adventure 2, problems in, 130
Adventure 2, problems of, 77
Adventure 2, procedures and function,
71, 72
Adventure 2, program outline, 69
Adventure 2, structure diagram, 74
Adventures, systematic approach to
writing, 256
Adventures, writer's checklist for, 256
AND, 131
Apple ll, 12
B
Backup disks, 143
Beings, 1, 4, 6
Block structured, 63
Boolean expressions, 131
Boolean operators, 131
Boolean variable, 130
Browse, 145
Browse program, 175, 181
c
Case statements, 59
Clues, 8
Cmdlookup function, 116, 118
Code file, 241
Command dispatcher, 116
Command handling code, 116
Command processing, 115
Commands, 115
Continuation file instruction lines, 170
Controlling the game, 60, 115
Conversion of representations, 120
Conversion of values, 119
Crowther, Willie, viii
D
Database, 137, 177, 250, 286, 292
Database concepts, 145
Database index, 145
Database of descriptions, creating a,
166
Declarations, local, 63
Description lines, 168
Descriptions, 190
Descriptions index, 177
Dig command, 138
Direction, deciding on a, 61
Ditto lines, 169
Docommand procedure, 116, 118
Dungeons and Dragons, ix
E
Eat command, 138
307
Editor limitations, 139 Makedese, running, 171 s
Embeliising the map, 6 Make80 database generator listing, Scanners, 116
Enumerated type, 54 147 Scoring, 137
Enumerated types, 118, 131 Map, 254 Search, linear, 121
Escape key, 143 Map layout, 6 Searching, 121, 122, 183
Events, 130 Map organization, 6 Semantics, 240
Exploration, 3 Maps, 192 Sentinels, 118, 123
Menu-driven, 13 Set, 125
F Microsoft, Inc. vii Set relationships, 131
File creating program, 174 Monsters, 1, 4, 6 Set variables, 127
File managing, 139 Sierra On-Line, ix
File reading program, 174 N Skeleton, 66
File variables, 137 F Skeleton adventure, 256
Files, loss of, 142-143 bath rican ae Skeleton adventure, listing for, 261
Files, random access, 173 Nocmd, 123 , Source file, 139, 140
Files, sequential, 173 NOT, 131 Special conditions, 2
‘ String comparison, 122
H Structured design, 16
Hash function, 184 0 Surprise, 10
Hashing, 182-183 Objects, 1, 3, 6 Swapping mode, 141
Hash table, 185 Objlookup, 128 Symbol tables, 182
Hash value, 183-184 Ogre, 138 Syntax, 240
Head string, 117, 120 Operator procedure, 132
Help command, 137 OR, 131 T
Tail string, 117, 120
I P Template, 66
Index files, 177 Travel, 136
Infocom, vii eras 18 Travel, simplification of, 136
arsers, 116 T Ryley
Pascal. ix. 12 ravel indicators, 5
K Pear : 127 Travel procedure, 116
K(runch command, 141 Bary pies dure, 128 Treasures, 1, 3
Pdrop, 127 Turns, counting of, 135
Pflames procedure, 67
Pisland procedure, 66 U
Unit, implementation part of, 241
Linear search, 121 ees 67 Unit, interface part of, 241
Linear searches, 183 P : Units, programming in, 239
Linked lists, 183 ee as USCD, ix, 13
Linking, 198 ate ih jaa USCD limitations, 252
Linking programs, 242 Poi : Uses statement, 242
F pit procedure, 67
Listen procedure, 116, 118 Preconditions, 132
Local procedures, 63-64 Prefix #5. 1 At v
Location, the adventurer’s, 60 Problem diffi culty, 9
Locations, 1, 2,5 Ys
; ; Problems, 2, 4, 6, 130
Locations, changing, 60-61 Pstart procedure, 67
Lamp command, 137
Light command, 137
Values, external, 119
Values, internal, 119
Variables, file, 137
Logic, 9
Logical connectives, 131 Pvestibule procedure, 67 Ww
Lookup techniques, 183 Whichway function, 116
R Woods, Don, viii
M Random access, 175 Writer's checklist, 256
Main program, 240 Relational operators, 131
Main program block, 17 Repeatability, 9 v4
Makedesc, 145, 167 Rooms, 1, 2, 5 ZORK, 1
308 Edited by Marilyn L. Johnson
Programming Your Own
Adventure Games in Pascal
If you are intrigued with the possibilities of the program included in Programming Your Own
Adventure Games in Pascal (TAB Book No. 1768), you should definitely consider having the
ready-to-run tape containing the software applications. This software is guaranteed free of man-
ufacturer’s defects. (If you have any problems, return the disk within 30 days, and we'll send you a
new one.) Not only will you save the time and effort of typing the programs, the disk eliminates the
possibility of errors that can prevent the programs from functioning. Interested?
Available on disk for Apple II, Il+ with 48K, Apple language card or other 16K RAM card, and Apple
Pascal system; Apple //e with Apple Pascal system at $19.95 for each tape or disk plus $1.00 each
shipping and handling.
ee er ar mS ee Pe ng ee tg eT ge ee eT ee
I’m interested. Send me:
disk for Apple Il, Il+, He (requires Apple Pascal system) (6229S)
TAB BOOKS catalog
Check/Money Order enclosed for $19.95
plus $1.00 shipping and handling for each tape or disk ordered.
VISA _______ MasterCard
Account No, ———_____—CO—C_.séEEpires
Name
Address
City) se ee SANS: ee ZIP
Signature
Mail to: TAB BOOKS Inc.
P.O. Box 40
Blue Ridge Summit, PA 17214
(Pa. add 6% sales tax. Orders outside U.S. must be prepaid with international money orders in U.S. dollars.)
TAB 1768
| ee
ie Sp ea Neste See a SE ah SS ea ee SE eS ee ee ea ee
PROGRAMMING YOUR OWN
ADVENTURE GAMES IN
PASCAL
BY RICHARD C. VILE, JR.
Imagine!
You can write
your own exciting
adventure games...
just like a pro!
TAB BOOKS inc.
Blue Ridge Summit, Pa. 17214
Send for FREE TAB Catalog describing over 750 current titles in print.
ISBN O-&306-0768-4
tM ‘
ammi 79 oun own adven
sed: Good
BUKAGaseseoe
|
TVWOSVd NI SAWVS AYNLNAAGV
NMO YNOA ONINWVYDOUd ~ bi:
ATA